package organization

import (
	"context"
	"crypto/aes"
	"crypto/cipher"
	"crypto/hmac"
	"crypto/rand"
	"crypto/sha256"
	"database/sql"
	"encoding/base64"
	"fmt"
	"io"
	"sort"
	"strings"
	"time"

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

const (
	organizationFeishuCredentialSourceOrganization = "organization"
	organizationFeishuCredentialSourceEnvironment  = "environment"
	feishuAuthStateSigningContext                  = "agentteam-org:feishu-auth-state:v1"
)

type organizationFeishuCredentialRecord struct {
	OrganizationID      string
	AppIDCiphertext     string
	AppSecretCiphertext string
	UpdatedAt           time.Time
	LastValidatedAt     sql.NullTime
}

type effectiveFeishuCredentials struct {
	AppID     string
	AppSecret string
	Source    string
}

func (txs TxStore) hasFeishuCredentialStorage() bool {
	return len(txs.feishuCredentialEncryptionKey) == 32
}

func (txs TxStore) GetOrganizationEnterpriseFeishuForUser(ctx context.Context, userID string, organizationID string) (OrganizationEnterpriseFeishuView, error) {
	if _, err := txs.requireActiveOrganizationAdmin(ctx, txs.db, userID, organizationID); err != nil {
		return OrganizationEnterpriseFeishuView{}, err
	}
	return txs.loadOrganizationEnterpriseFeishuView(ctx, txs.db, organizationID)
}

func (txs TxStore) UpdateOrganizationEnterpriseFeishuCredentialsForUser(ctx context.Context, userID string, organizationID string, appID string, appSecret string) (OrganizationEnterpriseFeishuView, error) {
	if _, err := txs.requireActiveOrganizationAdmin(ctx, txs.db, userID, organizationID); err != nil {
		return OrganizationEnterpriseFeishuView{}, err
	}
	if !txs.hasFeishuCredentialStorage() {
		return OrganizationEnterpriseFeishuView{}, badRequest("feishu credential storage is not configured on server")
	}

	resolvedAppID := strings.TrimSpace(appID)
	if resolvedAppID == "" {
		return OrganizationEnterpriseFeishuView{}, badRequest("feishu app id is required")
	}
	resolvedAppSecret := strings.TrimSpace(appSecret)
	if resolvedAppSecret == "" {
		return OrganizationEnterpriseFeishuView{}, badRequest("feishu app secret is required")
	}

	if err := txs.validateFeishuCredentialPair(ctx, resolvedAppID, resolvedAppSecret); err != nil {
		return OrganizationEnterpriseFeishuView{}, err
	}

	appIDCiphertext, err := encryptFeishuCredential(txs.feishuCredentialEncryptionKey, resolvedAppID)
	if err != nil {
		return OrganizationEnterpriseFeishuView{}, fmt.Errorf("encrypt feishu app id: %w", err)
	}
	appSecretCiphertext, err := encryptFeishuCredential(txs.feishuCredentialEncryptionKey, resolvedAppSecret)
	if err != nil {
		return OrganizationEnterpriseFeishuView{}, fmt.Errorf("encrypt feishu app secret: %w", err)
	}

	now := time.Now().UTC()
	if err := execPortableUpdateThenInsert(
		ctx,
		txs.db,
		`
SELECT 1
FROM organization_feishu_credentials
WHERE organization_id = $1
`,
		[]any{organizationID},
		`
UPDATE organization_feishu_credentials
SET app_id_ciphertext = $2,
    app_secret_ciphertext = $3,
    updated_by_user_id = $4,
    updated_at = $5,
    last_validated_at = $5
WHERE organization_id = $1
`,
		[]any{organizationID, appIDCiphertext, appSecretCiphertext, userID, now},
		`
INSERT INTO organization_feishu_credentials (
    organization_id,
    app_id_ciphertext,
    app_secret_ciphertext,
    updated_by_user_id,
    created_at,
    updated_at,
    last_validated_at
)
VALUES ($1, $2, $3, $4, $5, $5, $5)
`,
		[]any{organizationID, appIDCiphertext, appSecretCiphertext, userID, now},
	); err != nil {
		return OrganizationEnterpriseFeishuView{}, fmt.Errorf("upsert organization feishu credentials: %w", err)
	}

	return txs.loadOrganizationEnterpriseFeishuView(ctx, txs.db, organizationID)
}

func (txs TxStore) DeleteOrganizationEnterpriseFeishuCredentialsForUser(ctx context.Context, userID string, organizationID string) (OrganizationEnterpriseFeishuView, error) {
	if _, err := txs.requireActiveOrganizationAdmin(ctx, txs.db, userID, organizationID); err != nil {
		return OrganizationEnterpriseFeishuView{}, err
	}
	if _, err := txs.db.Exec(ctx, `
DELETE FROM organization_feishu_credentials
WHERE organization_id = $1
`, organizationID); err != nil {
		return OrganizationEnterpriseFeishuView{}, fmt.Errorf("delete organization feishu credentials: %w", err)
	}
	return txs.loadOrganizationEnterpriseFeishuView(ctx, txs.db, organizationID)
}

func (txs TxStore) loadOrganizationEnterpriseFeishuView(ctx context.Context, queryer queryRower, organizationID string) (OrganizationEnterpriseFeishuView, error) {
	record, found, err := txs.loadOrganizationFeishuCredentialRecord(ctx, queryer, organizationID)
	if err != nil {
		return OrganizationEnterpriseFeishuView{}, err
	}

	view := OrganizationEnterpriseFeishuView{
		OrganizationID: organizationID,
		StorageReady:   txs.hasFeishuCredentialStorage(),
	}
	if found {
		view.CredentialsConfigured = true
		view.CredentialSource = organizationFeishuCredentialSourceOrganization
		value := record.UpdatedAt
		view.UpdatedAt = &value
		if record.LastValidatedAt.Valid {
			validatedAt := record.LastValidatedAt.Time
			view.LastValidatedAt = &validatedAt
		}
		return txs.populateOrganizationEnterpriseFeishuDepartmentOptions(ctx, queryer, organizationID, view)
	}

	if txs.hasGlobalFeishuServerConfig() {
		view.CredentialsConfigured = true
		view.CredentialSource = organizationFeishuCredentialSourceEnvironment
	}
	return txs.populateOrganizationEnterpriseFeishuDepartmentOptions(ctx, queryer, organizationID, view)
}

func (txs TxStore) populateOrganizationEnterpriseFeishuDepartmentOptions(ctx context.Context, queryer queryRower, organizationID string, view OrganizationEnterpriseFeishuView) (OrganizationEnterpriseFeishuView, error) {
	if !view.CredentialsConfigured {
		return view, nil
	}
	if view.CredentialSource == organizationFeishuCredentialSourceOrganization && !view.StorageReady {
		view.AvailableRootDepartmentsError = "server-side Feishu credential storage is unavailable"
		return view, nil
	}

	credentials, err := txs.resolveOrganizationFeishuCredentials(ctx, queryer, organizationID)
	if err != nil {
		view.AvailableRootDepartmentsError = err.Error()
		return view, nil
	}
	client, err := NewFeishuDirectoryHTTPClient(FeishuDirectoryHTTPClientOptions{
		BaseURL:    txs.feishuBaseURL,
		AppID:      credentials.AppID,
		AppSecret:  credentials.AppSecret,
		HTTPClient: txs.feishuHTTPClient,
	})
	if err != nil {
		view.AvailableRootDepartmentsError = err.Error()
		return view, nil
	}

	scopes, err := client.ListAuthorizedDepartmentScopes(ctx)
	if err != nil {
		view.AvailableRootDepartmentsError = err.Error()
		return view, nil
	}

	options := make([]OrganizationFeishuDepartmentOptionView, 0, len(scopes))
	for _, scope := range scopes {
		departmentID := strings.TrimSpace(scope.DepartmentID)
		if departmentID == "" {
			continue
		}
		department, err := client.GetDepartment(ctx, departmentID)
		name := departmentID
		if err == nil {
			if resolvedName := strings.TrimSpace(department.Name); resolvedName != "" {
				name = resolvedName
			}
		}
		options = append(options, OrganizationFeishuDepartmentOptionView{
			DepartmentID: departmentID,
			Name:         name,
		})
	}
	sort.Slice(options, func(i, j int) bool {
		leftName := strings.TrimSpace(options[i].Name)
		rightName := strings.TrimSpace(options[j].Name)
		if leftName == rightName {
			return options[i].DepartmentID < options[j].DepartmentID
		}
		return leftName < rightName
	})
	view.AvailableRootDepartments = options
	return view, nil
}

func (txs TxStore) hasUsableOrganizationFeishuCredentials(ctx context.Context, queryer queryRower, organizationID string) (bool, error) {
	_, found, err := txs.loadOrganizationFeishuCredentialRecord(ctx, queryer, organizationID)
	if err != nil {
		return false, err
	}
	if found {
		return txs.hasFeishuCredentialStorage(), nil
	}
	return txs.hasGlobalFeishuServerConfig(), nil
}

func (txs TxStore) resolveOrganizationFeishuCredentials(ctx context.Context, queryer queryRower, organizationID string) (effectiveFeishuCredentials, error) {
	record, found, err := txs.loadOrganizationFeishuCredentialRecord(ctx, queryer, organizationID)
	if err != nil {
		return effectiveFeishuCredentials{}, err
	}
	if found {
		if !txs.hasFeishuCredentialStorage() {
			return effectiveFeishuCredentials{}, badRequest("organization feishu credentials are configured but server credential storage is unavailable")
		}
		appID, err := decryptFeishuCredential(txs.feishuCredentialEncryptionKey, record.AppIDCiphertext)
		if err != nil {
			return effectiveFeishuCredentials{}, fmt.Errorf("decrypt organization feishu app id: %w", err)
		}
		appSecret, err := decryptFeishuCredential(txs.feishuCredentialEncryptionKey, record.AppSecretCiphertext)
		if err != nil {
			return effectiveFeishuCredentials{}, fmt.Errorf("decrypt organization feishu app secret: %w", err)
		}
		if strings.TrimSpace(appID) == "" || strings.TrimSpace(appSecret) == "" {
			return effectiveFeishuCredentials{}, badRequest("organization feishu credentials are incomplete")
		}
		return effectiveFeishuCredentials{
			AppID:     strings.TrimSpace(appID),
			AppSecret: strings.TrimSpace(appSecret),
			Source:    organizationFeishuCredentialSourceOrganization,
		}, nil
	}
	if !txs.hasGlobalFeishuServerConfig() {
		return effectiveFeishuCredentials{}, badRequest("feishu credentials are not configured")
	}
	return effectiveFeishuCredentials{
		AppID:     strings.TrimSpace(txs.feishuAppID),
		AppSecret: strings.TrimSpace(txs.feishuAppSecret),
		Source:    organizationFeishuCredentialSourceEnvironment,
	}, nil
}

func (txs TxStore) loadOrganizationFeishuCredentialRecord(ctx context.Context, queryer queryRower, organizationID string) (organizationFeishuCredentialRecord, bool, error) {
	row := queryer.QueryRow(ctx, `
SELECT organization_id,
       app_id_ciphertext,
       app_secret_ciphertext,
       updated_at,
       last_validated_at
FROM organization_feishu_credentials
WHERE organization_id = $1
`, organizationID)

	var record organizationFeishuCredentialRecord
	if err := row.Scan(
		&record.OrganizationID,
		&record.AppIDCiphertext,
		&record.AppSecretCiphertext,
		&record.UpdatedAt,
		&record.LastValidatedAt,
	); err != nil {
		if coredb.IsNotFound(err) {
			return organizationFeishuCredentialRecord{}, false, nil
		}
		return organizationFeishuCredentialRecord{}, false, fmt.Errorf("load organization feishu credentials: %w", err)
	}
	return record, true, nil
}

func (txs TxStore) validateFeishuCredentialPair(ctx context.Context, appID string, appSecret string) error {
	client, err := NewFeishuDirectoryHTTPClient(FeishuDirectoryHTTPClientOptions{
		BaseURL:    txs.feishuBaseURL,
		AppID:      appID,
		AppSecret:  appSecret,
		HTTPClient: txs.feishuHTTPClient,
	})
	if err != nil {
		return err
	}
	if _, err := client.EnsureTenantAccessToken(ctx); err != nil {
		return badRequest("failed to validate feishu app credentials")
	}
	return nil
}

func (txs TxStore) feishuAuthStateSigningKey() ([]byte, error) {
	if txs.hasFeishuCredentialStorage() {
		mac := hmac.New(sha256.New, txs.feishuCredentialEncryptionKey)
		_, _ = mac.Write([]byte(feishuAuthStateSigningContext))
		return mac.Sum(nil), nil
	}
	trimmedSecret := strings.TrimSpace(txs.feishuAppSecret)
	if trimmedSecret != "" {
		return []byte(trimmedSecret), nil
	}
	return nil, badRequest("feishu auth state signing secret is unavailable")
}

func encryptFeishuCredential(key []byte, plaintext string) (string, error) {
	block, err := aes.NewCipher(key)
	if err != nil {
		return "", fmt.Errorf("create feishu credential cipher: %w", err)
	}
	aead, err := cipher.NewGCM(block)
	if err != nil {
		return "", fmt.Errorf("create feishu credential gcm: %w", err)
	}

	nonce := make([]byte, aead.NonceSize())
	if _, err := io.ReadFull(rand.Reader, nonce); err != nil {
		return "", fmt.Errorf("generate feishu credential nonce: %w", err)
	}

	sealed := aead.Seal(nonce, nonce, []byte(plaintext), nil)
	return base64.RawStdEncoding.EncodeToString(sealed), nil
}

func decryptFeishuCredential(key []byte, ciphertext string) (string, error) {
	block, err := aes.NewCipher(key)
	if err != nil {
		return "", fmt.Errorf("create feishu credential cipher: %w", err)
	}
	aead, err := cipher.NewGCM(block)
	if err != nil {
		return "", fmt.Errorf("create feishu credential gcm: %w", err)
	}

	payload, err := base64.RawStdEncoding.DecodeString(strings.TrimSpace(ciphertext))
	if err != nil {
		return "", fmt.Errorf("decode feishu credential ciphertext: %w", err)
	}
	if len(payload) < aead.NonceSize() {
		return "", fmt.Errorf("feishu credential ciphertext is too short")
	}

	nonce := payload[:aead.NonceSize()]
	encrypted := payload[aead.NonceSize():]
	plaintext, err := aead.Open(nil, nonce, encrypted, nil)
	if err != nil {
		return "", fmt.Errorf("decrypt feishu credential ciphertext: %w", err)
	}
	return string(plaintext), nil
}
