package identity

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

	"github.com/tidbcloud/AgentTeam/internal/core/authidentity"
	"github.com/tidbcloud/AgentTeam/internal/core/identifiers"
	"github.com/tidbcloud/AgentTeam/internal/coredb"
)

const (
	MobileVerificationAttemptStatusPending = "pending"
	MobileVerificationAttemptStatusSent    = "sent"
	MobileVerificationAttemptStatusFailed  = "failed"
)

type MobileVerificationAttempt struct {
	ID                string
	UserID            string
	Mobile            string
	Provider          string
	Status            string
	SenderIdentity    string
	ProviderMessageID string
	ProviderStatus    string
	ErrorMessage      string
	ExpiresAt         time.Time
	RequestedAt       time.Time
	CompletedAt       *time.Time
}

type ActiveMobileVerificationCode struct {
	Mobile    string
	ExpiresAt time.Time
}

type CompleteMobileVerificationAttemptParams struct {
	Provider          string
	Status            string
	SenderIdentity    string
	ProviderMessageID string
	ProviderStatus    string
	ErrorMessage      string
	CompletedAt       time.Time
}

func (store *Store) CreateMobileVerificationAttempt(ctx context.Context, userID string, mobile string, expiresAt time.Time) (MobileVerificationAttempt, error) {
	resolvedMobile, err := authidentity.NormalizeMobile(mobile)
	if err != nil {
		return MobileVerificationAttempt{}, err
	}

	attemptID, err := identifiers.NewEntityID("mvh")
	if err != nil {
		return MobileVerificationAttempt{}, fmt.Errorf("generate mobile verification attempt id: %w", err)
	}

	requestedAt := time.Now().UTC()
	if _, err := store.db.Exec(ctx, `
INSERT INTO user_mobile_verification_send_attempts (
    id,
    user_id,
    mobile,
    status,
    error_message,
    expires_at,
    requested_at
)
VALUES ($1, $2, $3, $4, '', $5, $6)
`, attemptID, userID, resolvedMobile, MobileVerificationAttemptStatusPending, expiresAt, requestedAt); err != nil {
		return MobileVerificationAttempt{}, fmt.Errorf("insert mobile verification send attempt: %w", err)
	}

	return MobileVerificationAttempt{
		ID:          attemptID,
		UserID:      userID,
		Mobile:      resolvedMobile,
		Status:      MobileVerificationAttemptStatusPending,
		ExpiresAt:   expiresAt,
		RequestedAt: requestedAt,
	}, nil
}

func (store *Store) CompleteMobileVerificationAttempt(ctx context.Context, attemptID string, params CompleteMobileVerificationAttemptParams) error {
	status := strings.TrimSpace(params.Status)
	if !isValidMobileVerificationAttemptStatus(status) {
		return fmt.Errorf("unsupported mobile verification attempt status %q", params.Status)
	}

	completedAt := params.CompletedAt.UTC()
	if completedAt.IsZero() {
		completedAt = time.Now().UTC()
	}

	result, err := store.db.Exec(ctx, `
UPDATE user_mobile_verification_send_attempts
SET provider = $2,
    status = $3,
    sender_identity = $4,
    provider_message_id = $5,
    provider_status = $6,
    error_message = $7,
    completed_at = $8
WHERE id = $1
`, attemptID,
		strings.TrimSpace(params.Provider),
		status,
		strings.TrimSpace(params.SenderIdentity),
		strings.TrimSpace(params.ProviderMessageID),
		strings.TrimSpace(params.ProviderStatus),
		strings.TrimSpace(params.ErrorMessage),
		completedAt,
	)
	if err != nil {
		return fmt.Errorf("update mobile verification send attempt: %w", err)
	}
	if result.RowsAffected() == 0 {
		return ErrNotFound
	}
	return nil
}

func (store *Store) ListMobileVerificationAttemptsForUser(ctx context.Context, userID string, limit int) ([]MobileVerificationAttempt, error) {
	if limit <= 0 {
		limit = 5
	}

	rows, err := store.db.Query(ctx, `
SELECT
    id,
    user_id,
    mobile,
    provider,
    status,
    sender_identity,
    provider_message_id,
    provider_status,
    error_message,
    expires_at,
    requested_at,
    completed_at
FROM user_mobile_verification_send_attempts
WHERE user_id = $1
ORDER BY requested_at DESC, id DESC
LIMIT $2
`, userID, limit)
	if err != nil {
		return nil, fmt.Errorf("list mobile verification send attempts: %w", err)
	}
	defer rows.Close()

	attempts := make([]MobileVerificationAttempt, 0, limit)
	for rows.Next() {
		var attempt MobileVerificationAttempt
		if err := rows.Scan(
			&attempt.ID,
			&attempt.UserID,
			&attempt.Mobile,
			&attempt.Provider,
			&attempt.Status,
			&attempt.SenderIdentity,
			&attempt.ProviderMessageID,
			&attempt.ProviderStatus,
			&attempt.ErrorMessage,
			&attempt.ExpiresAt,
			&attempt.RequestedAt,
			&attempt.CompletedAt,
		); err != nil {
			return nil, fmt.Errorf("scan mobile verification send attempt: %w", err)
		}
		attempts = append(attempts, attempt)
	}
	if err := rows.Err(); err != nil {
		return nil, fmt.Errorf("iterate mobile verification send attempts: %w", err)
	}
	return attempts, nil
}

func (store *Store) GetActiveMobileVerificationCodeForUser(ctx context.Context, userID string) (ActiveMobileVerificationCode, bool, error) {
	row := store.db.QueryRow(ctx, `
SELECT mobile, expires_at
FROM user_mobile_verification_tokens
WHERE user_id = $1
  AND consumed_at IS NULL
  AND invalidated_at IS NULL
  AND expires_at > NOW()
ORDER BY created_at DESC
LIMIT 1
`, userID)

	var activeCode ActiveMobileVerificationCode
	if err := row.Scan(&activeCode.Mobile, &activeCode.ExpiresAt); err != nil {
		if coredb.IsNotFound(err) {
			return ActiveMobileVerificationCode{}, false, nil
		}
		return ActiveMobileVerificationCode{}, false, fmt.Errorf("load active mobile verification code: %w", err)
	}
	return activeCode, true, nil
}

func isValidMobileVerificationAttemptStatus(status string) bool {
	switch status {
	case MobileVerificationAttemptStatusPending, MobileVerificationAttemptStatusSent, MobileVerificationAttemptStatusFailed:
		return true
	default:
		return false
	}
}
