package auth

import (
	"context"
	"errors"
	"time"

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

type PGRepository struct {
	db *pgxpool.Pool
}

func NewPGRepository(db *pgxpool.Pool) *PGRepository {
	return &PGRepository{db: db}
}

func (r *PGRepository) FindUserByUsername(ctx context.Context, username string) (UserRecord, error) {
	const query = `
		select u.id, u.username, u.display_name, r.name, u.password_hash, u.is_active
		from users u
		join roles r on r.id = u.role_id
		where u.username = $1 and u.deleted_at is null
	`

	row := r.db.QueryRow(ctx, query, username)
	record := UserRecord{}
	if err := row.Scan(&record.ID, &record.Username, &record.DisplayName, &record.Role, &record.PasswordHash, &record.IsActive); err != nil {
		return UserRecord{}, err
	}

	return record, nil
}

func (r *PGRepository) CreateSession(ctx context.Context, userID uuid.UUID, expiresAt time.Time, ipAddress string, userAgent string) (uuid.UUID, error) {
	const query = `
		insert into sessions (id, user_id, ip_address, user_agent, expires_at)
		values ($1, $2, nullif($3, '')::inet, $4, $5)
	`

	id := uuid.New()
	_, err := r.db.Exec(ctx, query, id, userID, ipAddress, userAgent, expiresAt)
	return id, err
}

func (r *PGRepository) StoreRefreshToken(ctx context.Context, sessionID uuid.UUID, userID uuid.UUID, tokenHash string, expiresAt time.Time) error {
	const query = `
		insert into refresh_tokens (id, session_id, user_id, token_hash, expires_at)
		values ($1, $2, $3, $4, $5)
	`

	_, err := r.db.Exec(ctx, query, uuid.New(), sessionID, userID, tokenHash, expiresAt)
	return err
}

func (r *PGRepository) FindRefreshToken(ctx context.Context, tokenHash string) (UserRecord, uuid.UUID, error) {
	const query = `
		select u.id, u.username, u.display_name, roles.name, u.password_hash, u.is_active, rt.session_id
		from refresh_tokens rt
		join users u on u.id = rt.user_id
		join roles on roles.id = u.role_id
		where rt.token_hash = $1 and rt.revoked_at is null and rt.expires_at > now()
	`

	record := UserRecord{}
	var sessionID uuid.UUID
	row := r.db.QueryRow(ctx, query, tokenHash)
	if err := row.Scan(&record.ID, &record.Username, &record.DisplayName, &record.Role, &record.PasswordHash, &record.IsActive, &sessionID); err != nil {
		return UserRecord{}, uuid.Nil, err
	}
	if !record.IsActive {
		return UserRecord{}, uuid.Nil, errors.New("user inactive")
	}

	return record, sessionID, nil
}

func (r *PGRepository) RevokeRefreshToken(ctx context.Context, tokenHash string) error {
	const query = `update refresh_tokens set revoked_at = now() where token_hash = $1 and revoked_at is null`
	_, err := r.db.Exec(ctx, query, tokenHash)
	return err
}

func (r *PGRepository) HasUsers(ctx context.Context) (bool, error) {
	var count int
	if err := r.db.QueryRow(ctx, `select count(*) from users where deleted_at is null`).Scan(&count); err != nil {
		return false, err
	}
	return count > 0, nil
}

func (r *PGRepository) CreateBootstrapAdmin(ctx context.Context, username, displayName, passwordHash string) (UserRecord, error) {
	const query = `
		insert into users (id, role_id, username, display_name, password_hash, is_active)
		values ($1, (select id from roles where name = 'admin'), $2, $3, $4, true)
		returning id, username, display_name, password_hash, is_active
	`

	record := UserRecord{Role: "admin"}
	err := r.db.QueryRow(ctx, query, uuid.New(), username, displayName, passwordHash).
		Scan(&record.ID, &record.Username, &record.DisplayName, &record.PasswordHash, &record.IsActive)
	return record, err
}
