package store

import (
	"context"
	"errors"
	"time"

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

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

// PgUserRepository 基于 PostgreSQL 的 UserRepository 实现。
type PgUserRepository struct {
	pool *pgxpool.Pool
}

// NewPgUserRepository 创建 PgUserRepository。
func NewPgUserRepository(pool *pgxpool.Pool) *PgUserRepository {
	return &PgUserRepository{pool: pool}
}

func (r *PgUserRepository) Create(ctx context.Context, username, passwordHash string) (string, error) {
	log := trace.FromContext(ctx)

	var id string
	err := r.pool.QueryRow(ctx,
		`INSERT INTO users (username, password_hash) VALUES ($1, $2) RETURNING id`,
		username, passwordHash,
	).Scan(&id)
	if err != nil {
		log.Errorw("create user failed", "username", username, "error", err)
		return "", err
	}

	log.Debugw("user created", "user_id", id, "username", username)
	return id, nil
}

func (r *PgUserRepository) FindByUsername(ctx context.Context, username string) (*User, error) {
	log := trace.FromContext(ctx)

	var u User
	err := r.pool.QueryRow(ctx,
		`SELECT id, username, password_hash, created_at, updated_at FROM users WHERE username = $1`,
		username,
	).Scan(&u.ID, &u.Username, &u.PasswordHash, &u.CreatedAt, &u.UpdatedAt)
	if errors.Is(err, pgx.ErrNoRows) {
		return nil, ErrUserNotFound
	}
	if err != nil {
		log.Errorw("find user by username failed", "username", username, "error", err)
		return nil, err
	}

	log.Debugw("user found by username", "user_id", u.ID, "username", username)
	return &u, nil
}

func (r *PgUserRepository) FindByID(ctx context.Context, id string) (*User, error) {
	log := trace.FromContext(ctx)

	var u User
	err := r.pool.QueryRow(ctx,
		`SELECT id, username, password_hash, created_at, updated_at FROM users WHERE id = $1`,
		id,
	).Scan(&u.ID, &u.Username, &u.PasswordHash, &u.CreatedAt, &u.UpdatedAt)
	if errors.Is(err, pgx.ErrNoRows) {
		return nil, ErrUserNotFound
	}
	if err != nil {
		log.Errorw("find user by id failed", "user_id", id, "error", err)
		return nil, err
	}

	log.Debugw("user found by id", "user_id", id)
	return &u, nil
}

func (r *PgUserRepository) SaveRefreshToken(ctx context.Context, userID, tokenHash string, expiresAt time.Time) error {
	log := trace.FromContext(ctx)

	_, err := r.pool.Exec(ctx,
		`INSERT INTO refresh_tokens (user_id, token_hash, expires_at) VALUES ($1, $2, $3)`,
		userID, tokenHash, expiresAt,
	)
	if err != nil {
		log.Errorw("save refresh token failed", "user_id", userID, "error", err)
		return err
	}

	log.Debugw("refresh token saved", "user_id", userID)
	return nil
}

func (r *PgUserRepository) FindRefreshToken(ctx context.Context, tokenHash string) (string, error) {
	log := trace.FromContext(ctx)

	var userID string
	err := r.pool.QueryRow(ctx,
		`SELECT user_id FROM refresh_tokens WHERE token_hash = $1 AND expires_at > NOW()`,
		tokenHash,
	).Scan(&userID)
	if errors.Is(err, pgx.ErrNoRows) {
		return "", ErrRefreshTokenNotFound
	}
	if err != nil {
		log.Errorw("find refresh token failed", "error", err)
		return "", err
	}

	log.Debugw("refresh token found", "user_id", userID)
	return userID, nil
}

func (r *PgUserRepository) DeleteRefreshToken(ctx context.Context, tokenHash string) error {
	log := trace.FromContext(ctx)

	_, err := r.pool.Exec(ctx,
		`DELETE FROM refresh_tokens WHERE token_hash = $1`,
		tokenHash,
	)
	if err != nil {
		log.Errorw("delete refresh token failed", "error", err)
		return err
	}

	log.Debugw("refresh token deleted")
	return nil
}

func (r *PgUserRepository) DeleteUserRefreshTokens(ctx context.Context, userID string) error {
	log := trace.FromContext(ctx)

	_, err := r.pool.Exec(ctx,
		`DELETE FROM refresh_tokens WHERE user_id = $1`,
		userID,
	)
	if err != nil {
		log.Errorw("delete user refresh tokens failed", "user_id", userID, "error", err)
		return err
	}

	log.Debugw("user refresh tokens deleted", "user_id", userID)
	return nil
}
