package datastore

import (
	"context"
	"database/sql"
	"errors"
	"time"

	"epigas.gitea.cloud/RiskRancher/core/pkg/domain"
)

// ErrNotFound is a standard error we can use across our handlers
var ErrNotFound = errors.New("record not found")

func (s *SQLiteStore) CreateUser(ctx context.Context, email, fullName, passwordHash, globalRole string) (*domain.User, error) {
	query := `INSERT INTO users (email, full_name, password_hash, global_role) VALUES (?, ?, ?, ?)`

	result, err := s.DB.ExecContext(ctx, query, email, fullName, passwordHash, globalRole)
	if err != nil {
		return nil, err
	}

	id, err := result.LastInsertId()
	if err != nil {
		return nil, err
	}

	return &domain.User{
		ID:           int(id),
		Email:        email,
		FullName:     fullName,
		PasswordHash: passwordHash,
		GlobalRole:   globalRole,
	}, nil
}

func (s *SQLiteStore) GetUserByEmail(ctx context.Context, email string) (*domain.User, error) {
	var user domain.User
	query := "SELECT id, email, password_hash, full_name, global_role FROM users WHERE email = ? AND is_active = 1"

	err := s.DB.QueryRowContext(ctx, query, email).Scan(
		&user.ID,
		&user.Email,
		&user.PasswordHash,
		&user.FullName,
		&user.GlobalRole,
	)

	if err != nil {
		if errors.Is(err, sql.ErrNoRows) {
			return nil, sql.ErrNoRows // Bouncer says no (either wrong email, or deactivated)
		}
		return nil, err
	}

	return &user, nil
}

func (s *SQLiteStore) CreateSession(ctx context.Context, token string, userID int, expiresAt time.Time) error {
	query := `INSERT INTO sessions (session_token, user_id, expires_at) VALUES (?, ?, ?)`
	_, err := s.DB.ExecContext(ctx, query, token, userID, expiresAt)
	return err
}

func (s *SQLiteStore) GetSession(ctx context.Context, token string) (*domain.Session, error) {
	query := `SELECT session_token, user_id, expires_at FROM sessions WHERE session_token = ?`

	var session domain.Session
	err := s.DB.QueryRowContext(ctx, query, token).Scan(
		&session.Token,
		&session.UserID,
		&session.ExpiresAt,
	)

	if err != nil {
		if errors.Is(err, sql.ErrNoRows) {
			return nil, ErrNotFound
		}
		return nil, err
	}

	return &session, nil
}

// GetUserByID fetches a user's full record, including their role
func (s *SQLiteStore) GetUserByID(ctx context.Context, id int) (*domain.User, error) {
	query := `SELECT id, email, full_name, password_hash, global_role FROM users WHERE id = ?`

	var user domain.User
	err := s.DB.QueryRowContext(ctx, query, id).Scan(
		&user.ID,
		&user.Email,
		&user.FullName,
		&user.PasswordHash,
		&user.GlobalRole,
	)

	if err != nil {
		if errors.Is(err, sql.ErrNoRows) {
			return nil, ErrNotFound
		}
		return nil, err
	}

	return &user, nil
}

// UpdateUserPassword allows an administrator to overwrite a forgotten password
func (s *SQLiteStore) UpdateUserPassword(ctx context.Context, id int, newPasswordHash string) error {
	query := `UPDATE users SET password_hash = ? WHERE id = ?`

	_, err := s.DB.ExecContext(ctx, query, newPasswordHash, id)
	return err
}

// UpdateUserRole promotes or demotes a user by updating their global_role.
func (s *SQLiteStore) UpdateUserRole(ctx context.Context, id int, newRole string) error {
	query := `UPDATE users SET global_role = ? WHERE id = ?`

	_, err := s.DB.ExecContext(ctx, query, newRole, id)
	return err
}

// DeactivateUserAndReassign securely offboards a user, kicks them out
func (s *SQLiteStore) DeactivateUserAndReassign(ctx context.Context, userID int) error {
	var email string
	if err := s.DB.QueryRowContext(ctx, "SELECT email FROM users WHERE id = ?", userID).Scan(&email); err != nil {
		return err
	}

	tx, err := s.DB.BeginTx(ctx, nil)
	if err != nil {
		return err
	}
	defer tx.Rollback()

	_, err = tx.ExecContext(ctx, `UPDATE users SET is_active = 0 WHERE id = ?`, userID)
	if err != nil {
		return err
	}

	_, err = tx.ExecContext(ctx, `DELETE FROM ticket_assignments WHERE assignee = ?`, email)
	if err != nil {
		return err
	}

	_, err = tx.ExecContext(ctx, `DELETE FROM sessions WHERE user_id = ?`, userID)
	if err != nil {
		return err
	}

	return tx.Commit()
}

// GetUserCount returns the total number of registered users in the system.
func (s *SQLiteStore) GetUserCount(ctx context.Context) (int, error) {
	var count int
	err := s.DB.QueryRowContext(ctx, `SELECT COUNT(*) FROM users`).Scan(&count)
	if err != nil {
		return 0, err
	}
	return count, nil
}

func (s *SQLiteStore) GetAllUsers(ctx context.Context) ([]*domain.User, error) {
	// Notice the return type is now []*domain.User
	rows, err := s.DB.QueryContext(ctx, "SELECT id, email, full_name, global_role FROM users WHERE is_active = 1")
	if err != nil {
		return nil, err
	}
	defer rows.Close()

	var users []*domain.User
	for rows.Next() {
		var u domain.User
		if err := rows.Scan(&u.ID, &u.Email, &u.FullName, &u.GlobalRole); err == nil {
			users = append(users, &u) // 🚀 Appending the memory address!
		}
	}
	return users, nil
}

// DeleteSession removes the token from the database so it can never be used again.
func (s *SQLiteStore) DeleteSession(ctx context.Context, token string) error {
	_, err := s.DB.ExecContext(ctx, `DELETE FROM sessions WHERE token = ?`, token)
	return err
}
