package store

import (
	"context"
	"fmt"
	"time"

	"github.com/google/uuid"
	"github.com/jackc/pgx/v5"
	"github.com/jackc/pgx/v5/pgxpool"

	"github.com/hhs/camtalk/internal/models"
	"github.com/hhs/camtalk/internal/trace"
)

// UserScenarioRepository 用户自建情景仓储接口。
type UserScenarioRepository interface {
	Create(ctx context.Context, scenario *models.UserScenario) error
	FindByID(ctx context.Context, id string) (*models.UserScenario, error)
	FindByIDAndUserID(ctx context.Context, id, userID string) (*models.UserScenario, error)
	FindByUserID(ctx context.Context, userID string) ([]*models.UserScenario, error)
	Update(ctx context.Context, scenario *models.UserScenario) error
	Delete(ctx context.Context, id string) error
	CountByUserID(ctx context.Context, userID string) (int, error)
}

// PostgresUserScenarioRepo PostgreSQL 实现。
type PostgresUserScenarioRepo struct {
	pool *pgxpool.Pool
}

// NewPostgresUserScenarioRepo 创建 PostgreSQL 用户情景仓储。
func NewPostgresUserScenarioRepo(pool *pgxpool.Pool) UserScenarioRepository {
	return &PostgresUserScenarioRepo{pool: pool}
}

// Create 创建用户情景。
func (r *PostgresUserScenarioRepo) Create(ctx context.Context, scenario *models.UserScenario) error {
	log := trace.FromContext(ctx)

	query := `
		INSERT INTO user_scenarios (id, user_id, name, icon, description, prompt, greeting, language, created_at, updated_at)
		VALUES ($1, $2, $3, $4, NULLIF($5, ''), $6, NULLIF($7, ''), $8, $9, $10)
		RETURNING id, created_at, updated_at
	`

	now := time.Now()
	scenario.CreatedAt = now
	scenario.UpdatedAt = now

	if scenario.ID == "" {
		scenario.ID = uuid.New().String()
	}
	if scenario.Icon == "" {
		scenario.Icon = "✨"
	}
	if scenario.Language == "" {
		scenario.Language = "zh-CN"
	}

	err := r.pool.QueryRow(ctx, query,
		scenario.ID,
		scenario.UserID,
		scenario.Name,
		scenario.Icon,
		scenario.Description,
		scenario.Prompt,
		scenario.Greeting,
		scenario.Language,
		scenario.CreatedAt,
		scenario.UpdatedAt,
	).Scan(&scenario.ID, &scenario.CreatedAt, &scenario.UpdatedAt)

	if err != nil {
		log.Errorw("create user scenario failed", "user_id", scenario.UserID, "name", scenario.Name, "error", err)
		return fmt.Errorf("create user scenario: %w", err)
	}

	log.Debugw("user scenario created", "scenario_id", scenario.ID, "user_id", scenario.UserID, "name", scenario.Name)
	return nil
}

// FindByID 根据 ID 查找情景。
func (r *PostgresUserScenarioRepo) FindByID(ctx context.Context, id string) (*models.UserScenario, error) {
	log := trace.FromContext(ctx)

	query := `
		SELECT id, user_id, name, icon, description, prompt, greeting, language, created_at, updated_at
		FROM user_scenarios
		WHERE id = $1
	`

	var scenario models.UserScenario
	err := r.pool.QueryRow(ctx, query, id).Scan(
		&scenario.ID,
		&scenario.UserID,
		&scenario.Name,
		&scenario.Icon,
		&scenario.Description,
		&scenario.Prompt,
		&scenario.Greeting,
		&scenario.Language,
		&scenario.CreatedAt,
		&scenario.UpdatedAt,
	)

	if err == pgx.ErrNoRows {
		return nil, fmt.Errorf("user scenario not found: %s", id)
	}
	if err != nil {
		log.Errorw("find user scenario failed", "scenario_id", id, "error", err)
		return nil, fmt.Errorf("find user scenario: %w", err)
	}

	log.Debugw("user scenario found", "scenario_id", id)
	return &scenario, nil
}

// FindByIDAndUserID 根据 ID 和用户 ID 查找情景（权限校验）。
func (r *PostgresUserScenarioRepo) FindByIDAndUserID(ctx context.Context, id, userID string) (*models.UserScenario, error) {
	log := trace.FromContext(ctx)

	query := `
		SELECT id, user_id, name, icon, description, prompt, greeting, language, created_at, updated_at
		FROM user_scenarios
		WHERE id = $1 AND user_id = $2
	`

	var scenario models.UserScenario
	err := r.pool.QueryRow(ctx, query, id, userID).Scan(
		&scenario.ID,
		&scenario.UserID,
		&scenario.Name,
		&scenario.Icon,
		&scenario.Description,
		&scenario.Prompt,
		&scenario.Greeting,
		&scenario.Language,
		&scenario.CreatedAt,
		&scenario.UpdatedAt,
	)

	if err == pgx.ErrNoRows {
		return nil, fmt.Errorf("user scenario not found or no permission")
	}
	if err != nil {
		log.Errorw("find user scenario by id and user failed", "scenario_id", id, "user_id", userID, "error", err)
		return nil, fmt.Errorf("find user scenario: %w", err)
	}

	log.Debugw("user scenario found by id and user", "scenario_id", id, "user_id", userID)
	return &scenario, nil
}

// FindByUserID 查找用户的所有情景。
func (r *PostgresUserScenarioRepo) FindByUserID(ctx context.Context, userID string) ([]*models.UserScenario, error) {
	log := trace.FromContext(ctx)

	query := `
		SELECT id, user_id, name, icon, description, prompt, greeting, language, created_at, updated_at
		FROM user_scenarios
		WHERE user_id = $1
		ORDER BY created_at DESC
	`

	rows, err := r.pool.Query(ctx, query, userID)
	if err != nil {
		log.Errorw("find user scenarios failed", "user_id", userID, "error", err)
		return nil, fmt.Errorf("find user scenarios: %w", err)
	}
	defer rows.Close()

	var scenarios []*models.UserScenario
	for rows.Next() {
		var s models.UserScenario
		err := rows.Scan(
			&s.ID,
			&s.UserID,
			&s.Name,
			&s.Icon,
			&s.Description,
			&s.Prompt,
			&s.Greeting,
			&s.Language,
			&s.CreatedAt,
			&s.UpdatedAt,
		)
		if err != nil {
			log.Errorw("scan user scenario row failed", "user_id", userID, "error", err)
			return nil, fmt.Errorf("scan user scenario: %w", err)
		}
		scenarios = append(scenarios, &s)
	}

	if err = rows.Err(); err != nil {
		log.Errorw("iterate user scenarios failed", "user_id", userID, "error", err)
		return nil, fmt.Errorf("iterate user scenarios: %w", err)
	}

	log.Debugw("user scenarios found", "user_id", userID, "count", len(scenarios))
	return scenarios, nil
}

// Update 更新用户情景。
func (r *PostgresUserScenarioRepo) Update(ctx context.Context, scenario *models.UserScenario) error {
	log := trace.FromContext(ctx)

	query := `
		UPDATE user_scenarios
		SET name = $1, icon = $2, description = $3, prompt = $4, greeting = $5, language = $6, updated_at = $7
		WHERE id = $8 AND user_id = $9
		RETURNING updated_at
	`

	scenario.UpdatedAt = time.Now()

	err := r.pool.QueryRow(ctx, query,
		scenario.Name,
		scenario.Icon,
		scenario.Description,
		scenario.Prompt,
		scenario.Greeting,
		scenario.Language,
		scenario.UpdatedAt,
		scenario.ID,
		scenario.UserID,
	).Scan(&scenario.UpdatedAt)

	if err == pgx.ErrNoRows {
		return fmt.Errorf("user scenario not found or no permission")
	}
	if err != nil {
		log.Errorw("update user scenario failed", "scenario_id", scenario.ID, "user_id", scenario.UserID, "error", err)
		return fmt.Errorf("update user scenario: %w", err)
	}

	log.Debugw("user scenario updated", "scenario_id", scenario.ID, "user_id", scenario.UserID)
	return nil
}

// Delete 删除用户情景。
func (r *PostgresUserScenarioRepo) Delete(ctx context.Context, id string) error {
	log := trace.FromContext(ctx)

	query := `DELETE FROM user_scenarios WHERE id = $1`

	result, err := r.pool.Exec(ctx, query, id)
	if err != nil {
		log.Errorw("delete user scenario failed", "scenario_id", id, "error", err)
		return fmt.Errorf("delete user scenario: %w", err)
	}

	if result.RowsAffected() == 0 {
		return fmt.Errorf("user scenario not found")
	}

	log.Debugw("user scenario deleted", "scenario_id", id)
	return nil
}

// CountByUserID 统计用户的情景数量。
func (r *PostgresUserScenarioRepo) CountByUserID(ctx context.Context, userID string) (int, error) {
	log := trace.FromContext(ctx)

	query := `SELECT COUNT(*) FROM user_scenarios WHERE user_id = $1`

	var count int
	err := r.pool.QueryRow(ctx, query, userID).Scan(&count)
	if err != nil {
		log.Errorw("count user scenarios failed", "user_id", userID, "error", err)
		return 0, fmt.Errorf("count user scenarios: %w", err)
	}

	log.Debugw("user scenarios counted", "user_id", userID, "count", count)
	return count, nil
}
