package mailstore

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

	"github.com/tidbcloud/AgentTeam/internal/core/identifiers"
	"github.com/tidbcloud/AgentTeam/internal/coredb"
	"github.com/tidbcloud/AgentTeam/internal/store/app"
	"github.com/tidbcloud/AgentTeam/internal/store/app/registry"
	"github.com/tidbcloud/AgentTeam/internal/store/app/runtime"
	"github.com/tidbcloud/AgentTeam/internal/store/serviceactor"
	"github.com/tidbcloud/AgentTeam/internal/store/storeerr"
)

type StatelessConnectionMaterial struct {
	SenderAddress      string
	ExternalSubjectRef string
	DisplayName        string
	ExtensionName      string
	Credentials        map[string]any
	Config             map[string]any
}

type StatelessInstallationView struct {
	Installation app.AppInstallationView
	Connection   runtime.ConnectionView
	Extension    serviceactor.InstallationExtension
}

type ReconnectIdentity struct {
	SenderAddress      string
	ExternalSubjectRef string
}

func (store *Store) FinalizeStatelessInstallation(
	ctx context.Context,
	userID string,
	workspaceID string,
	material StatelessConnectionMaterial,
) (StatelessInstallationView, error) {
	if err := store.validateStatelessMaterial(material, true); err != nil {
		return StatelessInstallationView{}, err
	}
	instanceKey, err := identifiers.NewEntityID("mailbox")
	if err != nil {
		return StatelessInstallationView{},
			fmt.Errorf("generate Email mailbox instance key: %w", err)
	}
	view, err :=
		store.apps.FinalizeNamedAppInstallationForWorkspaceAdmin(
			ctx,
			strings.TrimSpace(userID),
			strings.TrimSpace(workspaceID),
			app.NamedAppInstallationParams{
				AppID:       registry.EmailAppID,
				AppSlug:     registry.EmailAppSlug,
				InstanceKey: instanceKey,
				Handle: "email-" +
					strings.TrimPrefix(instanceKey, "mailbox_"),
				DisplayName:     material.DisplayName,
				ExtensionDomain: "mail",
				ExtensionName:   material.ExtensionName,
				Connection: app.NamedAppConnectionParams{
					ExternalSubjectRef: material.ExternalSubjectRef,
					DisplayLabel:       material.SenderAddress,
					Credentials: cloneJSONObject(
						material.Credentials,
					),
					Config: cloneJSONObject(material.Config),
				},
			},
			app.NamedAppMutationHooks{},
		)
	if err != nil {
		return StatelessInstallationView{}, err
	}
	return StatelessInstallationView{
		Installation: view.Installation,
		Connection:   view.Connection,
		Extension:    view.Extension,
	}, nil
}

func (store *Store) LoadStatelessReconnectIdentity(
	ctx context.Context,
	workspaceID string,
	installationID string,
) (ReconnectIdentity, error) {
	if err := store.validateMailboxMaterializer(); err != nil {
		return ReconnectIdentity{}, err
	}
	var identity ReconnectIdentity
	var configJSON []byte
	err := store.db.QueryRow(ctx, `
SELECT connection.external_subject_ref, connection.config
FROM service_installations installation
JOIN service_connections connection
  ON connection.service_installation_id = installation.id
 AND connection.workspace_id = installation.workspace_id
 AND connection.service_definition_id = installation.service_definition_id
WHERE installation.id = $1
  AND installation.workspace_id = $2
  AND installation.service_definition_id = $3
  AND installation.status = 'active'
  AND connection.status = 'active'
ORDER BY connection.updated_at DESC, connection.id DESC
LIMIT 1
`, strings.TrimSpace(installationID), strings.TrimSpace(workspaceID),
		registry.EmailAppID).Scan(
		&identity.ExternalSubjectRef,
		&configJSON,
	)
	if coredb.IsNotFound(err) {
		return ReconnectIdentity{}, storeerr.NotFound(
			"Email Installation connection is unavailable",
		)
	}
	if err != nil {
		return ReconnectIdentity{},
			fmt.Errorf("load Email reconnect identity: %w", err)
	}
	var config struct {
		SenderAddress string `json:"senderAddress"`
	}
	if err := json.Unmarshal(configJSON, &config); err != nil {
		return ReconnectIdentity{},
			storeerr.Conflict("Email Connection config is invalid")
	}
	identity.SenderAddress = strings.TrimSpace(config.SenderAddress)
	identity.ExternalSubjectRef = strings.TrimSpace(
		identity.ExternalSubjectRef,
	)
	if identity.SenderAddress == "" ||
		identity.ExternalSubjectRef == "" {
		return ReconnectIdentity{},
			storeerr.Conflict("Email Connection identity is invalid")
	}
	return identity, nil
}

func (store *Store) FinalizeStatelessReconnect(
	ctx context.Context,
	userID string,
	workspaceID string,
	installationID string,
	material StatelessConnectionMaterial,
) (runtime.ConnectionView, error) {
	if err := store.validateStatelessMaterial(material, false); err != nil {
		return runtime.ConnectionView{}, err
	}
	return store.apps.FinalizeNamedAppReconnectForWorkspaceAdmin(
		ctx,
		strings.TrimSpace(userID),
		strings.TrimSpace(workspaceID),
		app.NamedAppReconnectParams{
			AppID:          registry.EmailAppID,
			InstallationID: strings.TrimSpace(installationID),
			Connection: app.NamedAppConnectionParams{
				ExternalSubjectRef: material.ExternalSubjectRef,
				DisplayLabel:       material.SenderAddress,
				Credentials: cloneJSONObject(
					material.Credentials,
				),
				Config: cloneJSONObject(material.Config),
			},
		},
		app.NamedAppMutationHooks{},
	)
}

func (store *Store) DisconnectStatelessConnection(
	ctx context.Context,
	userID string,
	workspaceID string,
	installationID string,
	connectionID string,
) error {
	if err := store.validateMailboxMaterializer(); err != nil {
		return err
	}
	return store.apps.DisconnectNamedAppConnectionForWorkspaceAdmin(
		ctx,
		strings.TrimSpace(userID),
		strings.TrimSpace(workspaceID),
		registry.EmailAppID,
		strings.TrimSpace(installationID),
		strings.TrimSpace(connectionID),
		nil,
	)
}

func (store *Store) validateStatelessMaterial(
	material StatelessConnectionMaterial,
	requireNames bool,
) error {
	if err := store.validateMailboxMaterializer(); err != nil {
		return err
	}
	material.SenderAddress = strings.TrimSpace(material.SenderAddress)
	material.ExternalSubjectRef = strings.TrimSpace(
		material.ExternalSubjectRef,
	)
	material.DisplayName = strings.TrimSpace(material.DisplayName)
	material.ExtensionName = strings.TrimSpace(material.ExtensionName)
	if material.SenderAddress == "" ||
		material.ExternalSubjectRef == "" ||
		len(material.Credentials) == 0 ||
		len(material.Config) == 0 {
		return storeerr.BadRequest(
			"Email Connection material is incomplete",
		)
	}
	if requireNames &&
		(material.DisplayName == "" || material.ExtensionName == "") {
		return storeerr.BadRequest(
			"Email Installation names are required",
		)
	}
	if !requireNames &&
		(material.DisplayName != "" || material.ExtensionName != "") {
		return storeerr.BadRequest(
			"Email reconnect cannot change Installation names",
		)
	}
	return nil
}

func cloneJSONObject(value map[string]any) map[string]any {
	if len(value) == 0 {
		return map[string]any{}
	}
	cloned := make(map[string]any, len(value))
	for key, item := range value {
		cloned[key] = item
	}
	return cloned
}
