package user

import (
	"context"

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

type Repository interface {
	List(ctx context.Context) ([]User, error)
	Create(ctx context.Context, input CreateRequest, passwordHash string) (User, error)
	Update(ctx context.Context, userID uuid.UUID, input UpdateRequest, passwordHash *string) (User, error)
	Delete(ctx context.Context, userID uuid.UUID) error
	SetActive(ctx context.Context, userID uuid.UUID, active bool) error
}

type PGRepository struct {
	db *pgxpool.Pool
}

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

func (r *PGRepository) List(ctx context.Context) ([]User, error) {
	rows, err := r.db.Query(ctx, `
		select u.id, r.id, r.name, u.username::text, u.display_name, coalesce(u.email::text, ''), u.is_active
		from users u
		join roles r on r.id = u.role_id
		where u.deleted_at is null
		order by u.created_at desc
	`)
	if err != nil {
		return nil, err
	}
	defer rows.Close()

	var users []User
	for rows.Next() {
		var item User
		if err := rows.Scan(&item.ID, &item.RoleID, &item.RoleName, &item.Username, &item.DisplayName, &item.Email, &item.IsActive); err != nil {
			return nil, err
		}
		users = append(users, item)
	}
	return users, rows.Err()
}

func (r *PGRepository) Create(ctx context.Context, input CreateRequest, passwordHash string) (User, error) {
	const query = `
		insert into users (id, role_id, username, display_name, email, password_hash)
		values ($1, (select id from roles where name = $2), $3, $4, nullif($5, ''), $6)
		returning
			id,
			(select id from roles where name = $2),
			$2,
			username::text,
			display_name,
			coalesce(email::text, ''),
			is_active
	`

	item := User{RoleName: input.Role}
	err := r.db.QueryRow(ctx, query, uuid.New(), input.Role, input.Username, input.DisplayName, input.Email, passwordHash).
		Scan(&item.ID, &item.RoleID, &item.RoleName, &item.Username, &item.DisplayName, &item.Email, &item.IsActive)
	return item, err
}

func (r *PGRepository) SetActive(ctx context.Context, userID uuid.UUID, active bool) error {
	_, err := r.db.Exec(ctx, `update users set is_active = $2, updated_at = now() where id = $1`, userID, active)
	return err
}

func (r *PGRepository) Update(ctx context.Context, userID uuid.UUID, input UpdateRequest, passwordHash *string) (User, error) {
	const query = `
		update users u
		set
			role_id = coalesce((select id from roles where name = $2), u.role_id),
			display_name = coalesce($3, u.display_name),
			email = case when $4 is null then u.email else nullif($4, '')::citext end,
			password_hash = coalesce($5, u.password_hash),
			is_active = coalesce($6, u.is_active),
			updated_at = now()
		where u.id = $1 and u.deleted_at is null
		returning
			u.id,
			u.role_id,
			(select name from roles where id = u.role_id),
			u.username::text,
			u.display_name,
			coalesce(u.email::text, ''),
			u.is_active
	`

	var item User
	err := r.db.QueryRow(ctx, query, userID, input.Role, input.DisplayName, input.Email, passwordHash, input.IsActive).
		Scan(&item.ID, &item.RoleID, &item.RoleName, &item.Username, &item.DisplayName, &item.Email, &item.IsActive)
	return item, err
}

func (r *PGRepository) Delete(ctx context.Context, userID uuid.UUID) error {
	_, err := r.db.Exec(ctx, `update users set deleted_at = now(), updated_at = now() where id = $1 and deleted_at is null`, userID)
	return err
}
