package installation

import (
	"bytes"
	"context"
	"encoding/json"
	"fmt"
	"strings"

	"github.com/tidbcloud/AgentTeam/internal/coredb"
	"github.com/tidbcloud/AgentTeam/internal/store/model"
)

type serviceInstallationVersionSeed struct {
	Version     string
	CallbackURL string
	Manifest    []byte
}

type serviceInstallationMetadata struct {
	Version     string          `json:"version,omitempty"`
	CallbackURL string          `json:"callbackURL,omitempty"`
	Manifest    json.RawMessage `json:"manifest,omitempty"`
}

// LoadServiceInstallationMetadata loads the pinned App version row and encodes
// the canonical metadata stored with a service installation.
func LoadServiceInstallationMetadata(ctx context.Context, queryer coredb.QueryRower, appID string, installedVersion string) ([]byte, error) {
	if queryer == nil {
		return nil, fmt.Errorf("app installation queryer is required")
	}
	return loadServiceInstallationMetadata(ctx, queryer, appID, installedVersion, "")
}

// LoadServiceInstallationMetadataForUpdate is the locking current-read
// variant for install replay under the definition mutation lock.
func LoadServiceInstallationMetadataForUpdate(ctx context.Context, queryer coredb.QueryRower, appID string, installedVersion string) ([]byte, error) {
	if queryer == nil {
		return nil, fmt.Errorf("app installation queryer is required")
	}
	return loadServiceInstallationMetadata(ctx, queryer, appID, installedVersion, "\nFOR UPDATE")
}

func loadServiceInstallationMetadata(ctx context.Context, queryer coredb.QueryRower, appID string, installedVersion string, lockSuffix string) ([]byte, error) {
	seed, err := loadServiceInstallationVersionSeed(ctx, queryer, appID, installedVersion, lockSuffix)
	if err != nil {
		return nil, err
	}
	manifest := json.RawMessage(bytes.TrimSpace(seed.Manifest))
	if len(manifest) == 0 {
		manifest = nil
	}
	return model.MarshalJSONObject("app service installation metadata", serviceInstallationMetadata{
		Version:     seed.Version,
		CallbackURL: seed.CallbackURL,
		Manifest:    manifest,
	})
}

func loadServiceInstallationVersionSeed(ctx context.Context, queryer coredb.QueryRower, appID string, installedVersion string, lockSuffix string) (serviceInstallationVersionSeed, error) {
	trimmedVersion := strings.TrimSpace(installedVersion)
	var row coredb.Row
	if trimmedVersion == "" {
		row = queryer.QueryRow(ctx, `
SELECT
    version.version,
    version.callback_url,
    version.manifest
FROM service_definitions app
JOIN extension_definition_versions version
  ON version.service_definition_id = app.id
 AND version.version = app.latest_version
WHERE app.id = $1
`+lockSuffix+`
`, strings.TrimSpace(appID))
	} else {
		row = queryer.QueryRow(ctx, `
SELECT
    version.version,
    version.callback_url,
    version.manifest
FROM extension_definition_versions version
WHERE version.service_definition_id = $1
  AND version.version = $2
`+lockSuffix+`
`, strings.TrimSpace(appID), trimmedVersion)
	}

	var seed serviceInstallationVersionSeed
	if err := row.Scan(&seed.Version, &seed.CallbackURL, &seed.Manifest); err != nil {
		if coredb.IsNotFound(err) {
			return serviceInstallationVersionSeed{}, ErrNotFound
		}
		return serviceInstallationVersionSeed{}, fmt.Errorf("load service installation version metadata: %w", err)
	}
	seed.Version = strings.TrimSpace(seed.Version)
	seed.CallbackURL = strings.TrimSpace(seed.CallbackURL)
	return seed, nil
}
