package identity

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

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

type RegistrationAllowlistEntry struct {
	ID                   string
	Pattern              string
	Reason               string
	CreatedByUserID      string
	CreatedByEmail       string
	CreatedByDisplayName string
	CreatedAt            time.Time
	UpdatedAt            time.Time
	ExpiresAt            *time.Time
	DisabledAt           *time.Time
}

func (store *Store) ListActiveRegistrationAllowlistPatterns(ctx context.Context) ([]string, error) {
	rows, err := store.db.Query(ctx, `
SELECT pattern
FROM registration_allowlist_entries
WHERE disabled_at IS NULL
  AND (expires_at IS NULL OR expires_at > NOW())
ORDER BY created_at DESC, id ASC
`)
	if err != nil {
		if strings.Contains(err.Error(), "registration_allowlist_entries") {
			return nil, nil
		}
		return nil, fmt.Errorf("list active registration allowlist patterns: %w", err)
	}
	defer rows.Close()

	patterns := make([]string, 0)
	for rows.Next() {
		var pattern string
		if err := rows.Scan(&pattern); err != nil {
			return nil, fmt.Errorf("scan active registration allowlist pattern: %w", err)
		}
		patterns = append(patterns, pattern)
	}
	if err := rows.Err(); err != nil {
		return nil, fmt.Errorf("iterate active registration allowlist patterns: %w", err)
	}
	return patterns, nil
}

func (store *Store) ListRegistrationAllowlistEntriesForUser(ctx context.Context, userID string, query string, includeDisabled bool) ([]RegistrationAllowlistEntry, error) {
	if err := store.requireSystemAdminUser(ctx, userID); err != nil {
		return nil, err
	}

	normalizedQuery := "%" + strings.ToLower(strings.TrimSpace(query)) + "%"
	rows, err := store.db.Query(ctx, `
SELECT
    e.id,
    e.pattern,
    e.reason,
    e.created_by_user_id,
    creator.email,
    COALESCE(NULLIF(creator.display_name, ''), creator.name),
    e.created_at,
    e.updated_at,
    e.expires_at,
    e.disabled_at
FROM registration_allowlist_entries e
JOIN users creator ON creator.id = e.created_by_user_id
WHERE ($1 = '%%'
       OR LOWER(e.pattern) LIKE $1
       OR LOWER(creator.email) LIKE $1
       OR LOWER(COALESCE(creator.display_name, creator.name, '')) LIKE $1)
  AND ($2 = TRUE OR e.disabled_at IS NULL)
ORDER BY e.disabled_at IS NULL DESC, e.created_at DESC, e.id ASC
`, normalizedQuery, includeDisabled)
	if err != nil {
		return nil, fmt.Errorf("list registration allowlist entries: %w", err)
	}
	defer rows.Close()

	entries := make([]RegistrationAllowlistEntry, 0)
	for rows.Next() {
		entry, err := scanRegistrationAllowlistEntry(rows)
		if err != nil {
			return nil, err
		}
		entries = append(entries, entry)
	}
	if err := rows.Err(); err != nil {
		return nil, fmt.Errorf("iterate registration allowlist entries: %w", err)
	}
	return entries, nil
}

func (store *Store) CreateRegistrationAllowlistEntryForUser(ctx context.Context, userID string, pattern string, reason string, expiresAt *time.Time) (RegistrationAllowlistEntry, error) {
	if err := store.requireSystemAdminUser(ctx, userID); err != nil {
		return RegistrationAllowlistEntry{}, err
	}

	input, err := registration.NormalizeAllowlistEntryInput(pattern, reason)
	if err != nil {
		return RegistrationAllowlistEntry{}, badRequest("allowlist pattern must be an email address or @domain pattern")
	}
	now := time.Now().UTC()
	return withTxValue(ctx, store.db, func(tx coredb.Tx) (RegistrationAllowlistEntry, error) {
		var existingID string
		row := tx.QueryRow(ctx, `
SELECT id
FROM registration_allowlist_entries
WHERE pattern = $1
FOR UPDATE
`, input.Pattern)
		switch err := row.Scan(&existingID); {
		case err == nil:
			if _, err := tx.Exec(ctx, `
UPDATE registration_allowlist_entries
SET reason = $2,
    expires_at = $3,
    disabled_at = NULL,
    updated_at = $4
WHERE id = $1
`, existingID, input.Reason, expiresAt, now); err != nil {
				return RegistrationAllowlistEntry{}, fmt.Errorf("reactivate registration allowlist entry %q: %w", existingID, err)
			}
		case coredb.IsNotFound(err):
			entryID, idErr := identifiers.NewEntityID("ral")
			if idErr != nil {
				return RegistrationAllowlistEntry{}, fmt.Errorf("generate registration allowlist entry id: %w", idErr)
			}
			if _, err := tx.Exec(ctx, `
INSERT INTO registration_allowlist_entries (
    id,
    pattern,
    reason,
    created_by_user_id,
    expires_at,
    created_at,
    updated_at
) VALUES ($1, $2, $3, $4, $5, $6, $6)
`, entryID, input.Pattern, input.Reason, userID, expiresAt, now); err != nil {
				return RegistrationAllowlistEntry{}, fmt.Errorf("insert registration allowlist entry %q: %w", input.Pattern, err)
			}
			existingID = entryID
		default:
			return RegistrationAllowlistEntry{}, fmt.Errorf("load registration allowlist entry %q: %w", input.Pattern, err)
		}

		return loadRegistrationAllowlistEntry(ctx, tx, existingID)
	})
}

func (store *Store) DisableRegistrationAllowlistEntryForUser(ctx context.Context, userID string, entryID string) (RegistrationAllowlistEntry, error) {
	if err := store.requireSystemAdminUser(ctx, userID); err != nil {
		return RegistrationAllowlistEntry{}, err
	}

	commandTag, err := store.db.Exec(ctx, `
UPDATE registration_allowlist_entries
SET disabled_at = NOW(),
    updated_at = NOW()
WHERE id = $1
  AND disabled_at IS NULL
`, strings.TrimSpace(entryID))
	if err != nil {
		return RegistrationAllowlistEntry{}, fmt.Errorf("disable registration allowlist entry %q: %w", entryID, err)
	}
	if commandTag.RowsAffected() == 0 {
		return RegistrationAllowlistEntry{}, ErrNotFound
	}
	return store.GetRegistrationAllowlistEntryForUser(ctx, userID, entryID)
}

func (store *Store) GetRegistrationAllowlistEntryForUser(ctx context.Context, userID string, entryID string) (RegistrationAllowlistEntry, error) {
	if err := store.requireSystemAdminUser(ctx, userID); err != nil {
		return RegistrationAllowlistEntry{}, err
	}
	return loadRegistrationAllowlistEntry(ctx, store.db, entryID)
}

func loadRegistrationAllowlistEntry(ctx context.Context, queryer coredb.QueryRower, entryID string) (RegistrationAllowlistEntry, error) {
	row := queryer.QueryRow(ctx, `
SELECT
    e.id,
    e.pattern,
    e.reason,
    e.created_by_user_id,
    creator.email,
    COALESCE(NULLIF(creator.display_name, ''), creator.name),
    e.created_at,
    e.updated_at,
    e.expires_at,
    e.disabled_at
FROM registration_allowlist_entries e
JOIN users creator ON creator.id = e.created_by_user_id
WHERE e.id = $1
`, strings.TrimSpace(entryID))
	entry, err := scanRegistrationAllowlistEntry(row)
	if err != nil {
		if coredb.IsNotFound(err) {
			return RegistrationAllowlistEntry{}, ErrNotFound
		}
		return RegistrationAllowlistEntry{}, err
	}
	return entry, nil
}

func scanRegistrationAllowlistEntry(row coredb.Row) (RegistrationAllowlistEntry, error) {
	var entry RegistrationAllowlistEntry
	if err := row.Scan(
		&entry.ID,
		&entry.Pattern,
		&entry.Reason,
		&entry.CreatedByUserID,
		&entry.CreatedByEmail,
		&entry.CreatedByDisplayName,
		&entry.CreatedAt,
		&entry.UpdatedAt,
		&entry.ExpiresAt,
		&entry.DisabledAt,
	); err != nil {
		return RegistrationAllowlistEntry{}, fmt.Errorf("scan registration allowlist entry: %w", err)
	}
	return entry, nil
}
