package store

import (
	"database/sql"
	"errors"
	"fmt"

	"github.com/google/uuid"
)

// User represents an authenticated user stored in the database.
type User struct {
	ID           string `json:"id"`
	Username     string `json:"username"`
	PasswordHash string `json:"-"`
	Email        string `json:"email"`
	Role         string `json:"role"` // admin, viewer
	CreatedAt    string `json:"created_at"`
	UpdatedAt    string `json:"updated_at"`
}

// AuthSettings holds the authentication configuration (single-row pattern).
type AuthSettings struct {
	AuthMode         string `json:"auth_mode"` // local, oidc
	OIDCClientID     string `json:"oidc_client_id"`
	OIDCClientSecret string `json:"-"`
	OIDCIssuerURL    string `json:"oidc_issuer_url"`
	OIDCRedirectURL  string `json:"oidc_redirect_url"`
}

// CreateUser inserts a new user record.
func (s *Store) CreateUser(u User) (User, error) {
	u.ID = uuid.New().String()
	u.CreatedAt = Now()
	u.UpdatedAt = u.CreatedAt

	_, err := s.db.Exec(
		`INSERT INTO users (id, username, password_hash, email, role, created_at, updated_at)
		 VALUES (?, ?, ?, ?, ?, ?, ?)`,
		u.ID, u.Username, u.PasswordHash, u.Email, u.Role, u.CreatedAt, u.UpdatedAt,
	)
	if err != nil {
		return User{}, fmt.Errorf("insert user: %w", err)
	}
	return u, nil
}

// GetUserByID returns a single user by its ID.
func (s *Store) GetUserByID(id string) (User, error) {
	var u User
	err := s.db.QueryRow(
		`SELECT id, username, password_hash, email, role, created_at, updated_at
		 FROM users WHERE id = ?`, id,
	).Scan(&u.ID, &u.Username, &u.PasswordHash, &u.Email, &u.Role, &u.CreatedAt, &u.UpdatedAt)
	if errors.Is(err, sql.ErrNoRows) {
		return User{}, fmt.Errorf("user %s: %w", id, ErrNotFound)
	}
	if err != nil {
		return User{}, fmt.Errorf("query user: %w", err)
	}
	return u, nil
}

// GetUserByUsername returns a single user by username.
func (s *Store) GetUserByUsername(username string) (User, error) {
	var u User
	err := s.db.QueryRow(
		`SELECT id, username, password_hash, email, role, created_at, updated_at
		 FROM users WHERE username = ?`, username,
	).Scan(&u.ID, &u.Username, &u.PasswordHash, &u.Email, &u.Role, &u.CreatedAt, &u.UpdatedAt)
	if errors.Is(err, sql.ErrNoRows) {
		return User{}, fmt.Errorf("user %q: %w", username, ErrNotFound)
	}
	if err != nil {
		return User{}, fmt.Errorf("query user by username: %w", err)
	}
	return u, nil
}

// GetAllUsers returns every user ordered by username.
func (s *Store) GetAllUsers() ([]User, error) {
	rows, err := s.db.Query(
		`SELECT id, username, password_hash, email, role, created_at, updated_at
		 FROM users ORDER BY username`,
	)
	if err != nil {
		return nil, fmt.Errorf("query users: %w", err)
	}
	defer rows.Close()

	users := []User{}
	for rows.Next() {
		var u User
		if err := rows.Scan(&u.ID, &u.Username, &u.PasswordHash, &u.Email, &u.Role, &u.CreatedAt, &u.UpdatedAt); err != nil {
			return nil, fmt.Errorf("scan user: %w", err)
		}
		users = append(users, u)
	}
	return users, rows.Err()
}

// UpdateUser updates a user's mutable fields (username, email, role).
func (s *Store) UpdateUser(u User) error {
	u.UpdatedAt = Now()
	result, err := s.db.Exec(
		`UPDATE users SET username=?, email=?, role=?, updated_at=? WHERE id=?`,
		u.Username, u.Email, u.Role, u.UpdatedAt, u.ID,
	)
	if err != nil {
		return fmt.Errorf("update user: %w", err)
	}
	n, _ := result.RowsAffected()
	if n == 0 {
		return fmt.Errorf("user %s: %w", u.ID, ErrNotFound)
	}
	return nil
}

// UpdateUserPassword updates a user's password hash.
func (s *Store) UpdateUserPassword(id string, passwordHash string) error {
	ts := Now()
	result, err := s.db.Exec(
		`UPDATE users SET password_hash=?, updated_at=? WHERE id=?`,
		passwordHash, ts, id,
	)
	if err != nil {
		return fmt.Errorf("update user password: %w", err)
	}
	n, _ := result.RowsAffected()
	if n == 0 {
		return fmt.Errorf("user %s: %w", id, ErrNotFound)
	}
	return nil
}

// DeleteUser removes a user by ID.
func (s *Store) DeleteUser(id string) error {
	result, err := s.db.Exec(`DELETE FROM users WHERE id = ?`, id)
	if err != nil {
		return fmt.Errorf("delete user: %w", err)
	}
	n, _ := result.RowsAffected()
	if n == 0 {
		return fmt.Errorf("user %s: %w", id, ErrNotFound)
	}
	return nil
}

// UserCount returns the total number of users.
func (s *Store) UserCount() (int, error) {
	var count int
	err := s.db.QueryRow(`SELECT COUNT(*) FROM users`).Scan(&count)
	if err != nil {
		return 0, fmt.Errorf("count users: %w", err)
	}
	return count, nil
}

// GetAuthSettings returns the auth settings (single-row pattern, always row id=1).
func (s *Store) GetAuthSettings() (AuthSettings, error) {
	var as AuthSettings
	err := s.db.QueryRow(
		`SELECT auth_mode, oidc_client_id, oidc_client_secret, oidc_issuer_url, oidc_redirect_url
		 FROM auth_settings WHERE id = 1`,
	).Scan(&as.AuthMode, &as.OIDCClientID, &as.OIDCClientSecret, &as.OIDCIssuerURL, &as.OIDCRedirectURL)
	if err != nil {
		return AuthSettings{}, fmt.Errorf("query auth settings: %w", err)
	}
	return as, nil
}

// UpdateAuthSettings updates the auth settings row.
func (s *Store) UpdateAuthSettings(as AuthSettings) error {
	_, err := s.db.Exec(
		`UPDATE auth_settings SET auth_mode=?, oidc_client_id=?, oidc_client_secret=?, oidc_issuer_url=?, oidc_redirect_url=?
		 WHERE id = 1`,
		as.AuthMode, as.OIDCClientID, as.OIDCClientSecret, as.OIDCIssuerURL, as.OIDCRedirectURL,
	)
	if err != nil {
		return fmt.Errorf("update auth settings: %w", err)
	}
	return nil
}
