package installation

import (
	"context"
	"fmt"
	"strings"

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

// LoadAppInstallationByAppForUpdate locks the singleton/default App
// installation row for an installation transaction. Named installations must
// use LoadAppInstallationByAppInstanceForUpdate. Not-found is an optional
// result for provider reinstall flows.
func LoadAppInstallationByAppForUpdate(ctx context.Context, queryer coredb.QueryRower, workspaceID string, appID string) (model.StoredAppInstallation, bool, error) {
	return LoadAppInstallationByAppInstanceForUpdate(
		ctx,
		queryer,
		workspaceID,
		appID,
		"default",
	)
}

// LoadAppInstallationByAppInstanceForUpdate locks one canonical installation
// row by its durable App instance identity.
func LoadAppInstallationByAppInstanceForUpdate(
	ctx context.Context,
	queryer coredb.QueryRower,
	workspaceID string,
	appID string,
	instanceKey string,
) (model.StoredAppInstallation, bool, error) {
	row := queryer.QueryRow(ctx, `
SELECT
    si.id,
    si.service_definition_id,
    sd.slug,
    sd.name,
    si.workspace_id,
    si.instance_key,
    COALESCE(sm.member_id, ''),
    COALESCE(sm.handle, ''),
    COALESCE(NULLIF(sm.display_name, ''), sd.name),
    si.installed_version,
    si.config,
    (si.status = 'active'),
    COALESCE(si.created_by_actor_id, si.created_by_member_id, ''),
    si.created_at,
    si.updated_at
FROM service_installations si
JOIN service_definitions sd ON sd.id = si.service_definition_id
LEFT JOIN service_members sm
  ON sm.service_installation_id = si.id
 AND sm.workspace_id = si.workspace_id
 AND sm.service_kind = si.service_kind
WHERE si.workspace_id = $1
  AND si.service_definition_id = $2
  AND si.instance_key = $3
  AND si.service_kind = 'app'
FOR UPDATE
`, strings.TrimSpace(workspaceID), strings.TrimSpace(appID), strings.TrimSpace(instanceKey))

	installation, err := ScanStoredAppInstallation(row)
	if err != nil {
		if coredb.IsNotFound(err) {
			return model.StoredAppInstallation{}, false, nil
		}
		return model.StoredAppInstallation{}, false, fmt.Errorf("load app installation by app instance: %w", err)
	}
	return installation, true, nil
}

// LoadAppInstallationForUpdate locks an installation row for update.
func LoadAppInstallationForUpdate(ctx context.Context, queryer coredb.QueryRower, workspaceID string, installationID string) (model.StoredAppInstallation, error) {
	row := queryer.QueryRow(ctx, `
SELECT
    si.id,
    si.service_definition_id,
    sd.slug,
    sd.name,
    si.workspace_id,
    si.instance_key,
    COALESCE(sm.member_id, ''),
    COALESCE(sm.handle, ''),
    COALESCE(NULLIF(sm.display_name, ''), sd.name),
    si.installed_version,
    si.config,
    (si.status = 'active'),
    COALESCE(si.created_by_actor_id, si.created_by_member_id, ''),
    si.created_at,
    si.updated_at
FROM service_installations si
JOIN service_definitions sd ON sd.id = si.service_definition_id
LEFT JOIN service_members sm
  ON sm.service_installation_id = si.id
 AND sm.workspace_id = si.workspace_id
 AND sm.service_kind = si.service_kind
WHERE si.workspace_id = $1
  AND si.id = $2
  AND si.service_kind = 'app'
FOR UPDATE
`, strings.TrimSpace(workspaceID), strings.TrimSpace(installationID))

	installation, err := ScanStoredAppInstallation(row)
	if err != nil {
		if coredb.IsNotFound(err) {
			return model.StoredAppInstallation{}, ErrNotFound
		}
		return model.StoredAppInstallation{}, fmt.Errorf("load app installation: %w", err)
	}
	return installation, nil
}

func ScanStoredAppInstallation(row coredb.Row) (model.StoredAppInstallation, error) {
	var installation model.StoredAppInstallation
	err := row.Scan(
		&installation.ID,
		&installation.AppID,
		&installation.AppSlug,
		&installation.AppName,
		&installation.WorkspaceID,
		&installation.InstanceKey,
		&installation.MemberID,
		&installation.Handle,
		&installation.MemberDisplayName,
		&installation.InstalledVersion,
		&installation.Config,
		&installation.Enabled,
		&installation.CreatedByUserID,
		&installation.CreatedAt,
		&installation.UpdatedAt,
	)
	if err != nil {
		return model.StoredAppInstallation{}, err
	}
	return installation, nil
}
