package service

import (
	"context"
	"fmt"
	"log/slog"
	"time"

	"github.com/TopherMayor/unified-media-manager/internal/db"
)

type User struct {
	ID          int64     `json:"id"`
	Username    string    `json:"username"`
	DisplayName string    `json:"display_name"`
	Role        string    `json:"role"` // admin, power_user, user
	APIKey      string    `json:"-"`
	CreatedAt   time.Time `json:"created_at"`
}

type UserResponse struct {
	ID          int64     `json:"id"`
	Username    string    `json:"username"`
	DisplayName string    `json:"display_name"`
	Role        string    `json:"role"`
	CreatedAt   time.Time `json:"created_at"`
}

const userColumns = `id, username, display_name, role, api_key, created_at`

type UserService struct {
	db *db.DB
}

func NewUserService(database *db.DB) *UserService {
	return &UserService{db: database}
}

func userToResponse(u *User) UserResponse {
	return UserResponse{
		ID:          u.ID,
		Username:    u.Username,
		DisplayName: u.DisplayName,
		Role:        u.Role,
		CreatedAt:   u.CreatedAt,
	}
}

func (s *UserService) SeedAdmin(ctx context.Context, apiKey string) error {
	if apiKey == "" {
		slog.Warn("ADMIN_API_KEY not set, skipping admin seed")
		return nil
	}

	tag, err := s.db.Pool.Exec(ctx,
		`INSERT INTO users (username, display_name, role, api_key)
		 VALUES ('admin', 'Administrator', 'admin', $1)
		 ON CONFLICT (username) DO NOTHING`, apiKey)
	if err != nil {
		return fmt.Errorf("seed admin: %w", err)
	}

	if tag.RowsAffected() > 0 {
		slog.Info("seeded admin user")
	}
	return nil
}

func (s *UserService) GetUserByAPIKey(ctx context.Context, apiKey string) (*User, error) {
	row := s.db.Pool.QueryRow(ctx,
		fmt.Sprintf("SELECT %s FROM users WHERE api_key = $1", userColumns), apiKey)

	var u User
	err := row.Scan(&u.ID, &u.Username, &u.DisplayName, &u.Role, &u.APIKey, &u.CreatedAt)
	if err != nil {
		return nil, fmt.Errorf("user not found")
	}
	return &u, nil
}

func (s *UserService) List(ctx context.Context) ([]UserResponse, error) {
	rows, err := s.db.Pool.Query(ctx,
		fmt.Sprintf("SELECT %s FROM users ORDER BY id", userColumns))
	if err != nil {
		return nil, fmt.Errorf("list users: %w", err)
	}
	defer rows.Close()

	var items []UserResponse
	for rows.Next() {
		var u User
		if err := rows.Scan(&u.ID, &u.Username, &u.DisplayName, &u.Role, &u.APIKey, &u.CreatedAt); err != nil {
			slog.Error("failed to scan user", "error", err)
			continue
		}
		items = append(items, userToResponse(&u))
	}
	return items, nil
}

func (s *UserService) GetByID(ctx context.Context, id int64) (*User, error) {
	row := s.db.Pool.QueryRow(ctx,
		fmt.Sprintf("SELECT %s FROM users WHERE id = $1", userColumns), id)

	var u User
	err := row.Scan(&u.ID, &u.Username, &u.DisplayName, &u.Role, &u.APIKey, &u.CreatedAt)
	if err != nil {
		return nil, fmt.Errorf("user not found")
	}
	return &u, nil
}

// GetUser returns the UserResponse (without API key) for display.
func (s *UserService) GetUser(ctx context.Context, id int64) (*UserResponse, error) {
	u, err := s.GetByID(ctx, id)
	if err != nil {
		return nil, err
	}
	resp := userToResponse(u)
	return &resp, nil
}
