package organization

import (
	"context"
	"fmt"
	"strings"
	"time"

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

type organizationFeishuAuthTarget struct {
	OrganizationID   string
	OrganizationSlug string
	Settings         organizationSettingsRecord
}

func (txs TxStore) BuildFeishuLoginURL(ctx context.Context, organizationSlug string, redirectURI string) (string, error) {
	target, err := txs.loadOrganizationFeishuAuthTargetBySlug(ctx, txs.db, organizationSlug)
	if err != nil {
		return "", err
	}
	credentials, err := txs.resolveOrganizationFeishuCredentials(ctx, txs.db, target.OrganizationID)
	if err != nil {
		return "", err
	}
	client, err := txs.newFeishuAuthClient(credentials)
	if err != nil {
		return "", err
	}
	signingKey, err := txs.feishuAuthStateSigningKey()
	if err != nil {
		return "", err
	}

	state, err := encodeFeishuAuthState(signingKey, target.OrganizationSlug, time.Now().UTC())
	if err != nil {
		return "", err
	}
	return client.BuildAuthorizeURL(redirectURI, state)
}

func (txs TxStore) AuthenticateFeishuLogin(ctx context.Context, code string, state string, redirectURI string) (model.User, error) {
	signingKey, err := txs.feishuAuthStateSigningKey()
	if err != nil {
		return model.User{}, err
	}
	statePayload, err := decodeFeishuAuthState(signingKey, state, time.Now().UTC())
	if err != nil {
		return model.User{}, err
	}

	target, err := txs.loadOrganizationFeishuAuthTargetBySlug(ctx, txs.db, statePayload.OrganizationSlug)
	if err != nil {
		return model.User{}, err
	}
	credentials, err := txs.resolveOrganizationFeishuCredentials(ctx, txs.db, target.OrganizationID)
	if err != nil {
		return model.User{}, err
	}
	client, err := txs.newFeishuAuthClient(credentials)
	if err != nil {
		return model.User{}, err
	}

	identity, err := client.ExchangeCode(ctx, code, redirectURI)
	if err != nil {
		return model.User{}, err
	}
	if target.Settings.Feishu.TenantKey != "" && identity.TenantKey != "" && identity.TenantKey != target.Settings.Feishu.TenantKey {
		return model.User{}, badRequest("feishu auth tenant does not match organization")
	}

	account, err := txs.resolveFeishuAuthAccount(ctx, target.OrganizationID, identity)
	if err != nil {
		return model.User{}, err
	}

	providerTenantID := strings.TrimSpace(identity.TenantKey)
	if providerTenantID == "" {
		providerTenantID = strings.TrimSpace(target.Settings.Feishu.TenantKey)
	}
	if err := txs.persistDiscoveredFeishuTenantKey(ctx, target.OrganizationID, target.Settings, providerTenantID); err != nil {
		return model.User{}, err
	}
	if err := txs.recordFeishuAuthBindings(ctx, target.OrganizationID, account.MemberID, providerTenantID, identity); err != nil {
		return model.User{}, err
	}

	if strings.TrimSpace(account.UserID) == "" {
		return model.User{}, ErrNotFound
	}
	user, err := txs.core.FindUserByID(ctx, account.UserID)
	if err != nil {
		return model.User{}, err
	}
	return user, nil
}

func (txs TxStore) newFeishuAuthClient(credentials effectiveFeishuCredentials) (*FeishuAuthHTTPClient, error) {
	return NewFeishuAuthHTTPClient(FeishuAuthHTTPClientOptions{
		BaseURL:    txs.feishuBaseURL,
		AppID:      credentials.AppID,
		AppSecret:  credentials.AppSecret,
		HTTPClient: txs.feishuHTTPClient,
	})
}

func (txs TxStore) loadOrganizationFeishuAuthTargetBySlug(ctx context.Context, queryer queryRower, organizationSlug string) (organizationFeishuAuthTarget, error) {
	trimmedSlug := strings.TrimSpace(organizationSlug)
	if trimmedSlug == "" {
		return organizationFeishuAuthTarget{}, badRequest("organization slug is required")
	}

	row := queryer.QueryRow(ctx, `
SELECT o.id,
       o.slug,
       os.directory_backend,
       os.directory_revision,
       os.auth_provider,
       os.directory_provider,
       os.feishu_tenant_key,
       os.feishu_root_department_id
FROM organizations o
JOIN organization_settings os ON os.organization_id = o.id
WHERE o.slug = $1
  AND os.status = 'active'
`, trimmedSlug)

	var target organizationFeishuAuthTarget
	if err := row.Scan(
		&target.OrganizationID,
		&target.OrganizationSlug,
		&target.Settings.DirectoryBackend,
		&target.Settings.DirectoryRevision,
		&target.Settings.AuthProvider,
		&target.Settings.DirectoryProvider,
		&target.Settings.Feishu.TenantKey,
		&target.Settings.Feishu.RootDepartmentID,
	); err != nil {
		if coredb.IsNotFound(err) {
			return organizationFeishuAuthTarget{}, ErrNotFound
		}
		return organizationFeishuAuthTarget{}, fmt.Errorf("load feishu auth target: %w", err)
	}
	target.Settings.OrganizationID = target.OrganizationID
	if target.Settings.AuthProvider != organizationProviderFeishu {
		return organizationFeishuAuthTarget{}, badRequest("organization auth provider is not feishu")
	}
	return target, nil
}

func (txs TxStore) resolveFeishuAuthAccount(ctx context.Context, organizationID string, identity FeishuAuthIdentity) (activeOrganizationMember, error) {
	if strings.TrimSpace(identity.OpenID) != "" {
		binding, found, err := txs.findOrganizationAuthBinding(ctx, txs.db, organizationID, organizationProviderFeishu, organizationAuthSubjectKeyOpenID, identity.OpenID)
		if err != nil {
			return activeOrganizationMember{}, err
		}
		if found {
			return txs.loadActiveOrganizationMember(ctx, txs.db, organizationID, binding.OrganizationMemberID)
		}
	}

	if strings.TrimSpace(identity.UnionID) != "" {
		binding, found, err := txs.findOrganizationAuthBinding(ctx, txs.db, organizationID, organizationProviderFeishu, organizationAuthSubjectKeyUnionID, identity.UnionID)
		if err != nil {
			return activeOrganizationMember{}, err
		}
		if found {
			return txs.loadActiveOrganizationMember(ctx, txs.db, organizationID, binding.OrganizationMemberID)
		}
	}

	userID := strings.TrimSpace(identity.UserID)
	if userID == "" {
		return activeOrganizationMember{}, badRequest("feishu auth identity is missing user id; ensure auth scope includes contact:user.employee_id:readonly and the permission is enabled for the Feishu app")
	}
	return txs.loadActiveOrganizationMemberByExternalID(ctx, txs.db, organizationID, organizationProviderFeishu, userID)
}

func (txs TxStore) recordFeishuAuthBindings(ctx context.Context, organizationID string, accountID string, providerTenantID string, identity FeishuAuthIdentity) error {
	if strings.TrimSpace(identity.OpenID) != "" {
		if _, err := txs.UpsertOrganizationMemberAuthBinding(ctx, organizationID, accountID, UpsertOrganizationAuthBindingParams{
			AuthProvider:     organizationProviderFeishu,
			SubjectKey:       organizationAuthSubjectKeyOpenID,
			SubjectValue:     identity.OpenID,
			ProviderTenantID: providerTenantID,
		}); err != nil {
			return err
		}
	}
	if strings.TrimSpace(identity.UnionID) != "" {
		if _, err := txs.UpsertOrganizationMemberAuthBinding(ctx, organizationID, accountID, UpsertOrganizationAuthBindingParams{
			AuthProvider:     organizationProviderFeishu,
			SubjectKey:       organizationAuthSubjectKeyUnionID,
			SubjectValue:     identity.UnionID,
			ProviderTenantID: providerTenantID,
		}); err != nil {
			return err
		}
	}
	return nil
}

func (txs TxStore) persistDiscoveredFeishuTenantKey(ctx context.Context, organizationID string, settings organizationSettingsRecord, tenantKey string) error {
	resolvedTenantKey := strings.TrimSpace(tenantKey)
	if resolvedTenantKey == "" || strings.TrimSpace(settings.Feishu.TenantKey) != "" {
		return nil
	}

	if _, err := txs.db.Exec(ctx, `
UPDATE organization_settings
SET feishu_tenant_key = $2,
    updated_at = NOW()
WHERE organization_id = $1
  AND COALESCE(feishu_tenant_key, '') = ''
`, organizationID, resolvedTenantKey); err != nil {
		return fmt.Errorf("persist discovered feishu tenant key: %w", err)
	}
	return nil
}

func (txs TxStore) loadActiveOrganizationMemberByExternalID(ctx context.Context, queryer queryRower, organizationID string, externalProvider string, externalID string) (activeOrganizationMember, error) {
	row := queryer.QueryRow(ctx, `
SELECT id, user_id, source, default_workspace_role, sync_mode
FROM organization_members
WHERE organization_id = $1
  AND external_provider = $2
  AND external_id = $3
  AND status = 'active'
`, organizationID, externalProvider, externalID)

	var account activeOrganizationMember
	if err := row.Scan(&account.MemberID, &account.UserID, &account.Source, &account.DefaultWorkspaceRole, &account.SyncMode); err != nil {
		if coredb.IsNotFound(err) {
			return activeOrganizationMember{}, badRequest("feishu directory user is not imported for organization")
		}
		return activeOrganizationMember{}, fmt.Errorf("load organization member by external id: %w", err)
	}
	return account, nil
}
