package games_repo

import (
	"context"
	"errors"
	"evening_detective_server/internal/repos"
	"time"

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

var (
	ErrGameNotFound = errors.New("Игра не найдена")
)

type GamesRepo struct {
	pool *pgxpool.Pool
}

func NewGamesRepo(pool *pgxpool.Pool) *GamesRepo {
	return &GamesRepo{
		pool: pool,
	}
}

func (r *GamesRepo) AddGame(
	ctx context.Context,
	name string,
	description string,
	startAt time.Time,
	scenarioId int32,
) (int, error) {
	id := 0
	err := r.pool.QueryRow(
		ctx,
		`INSERT INTO games (name, description, start_at, scenario_id)
		VALUES ($1, $2, $3, $4)
		RETURNING id`,
		name,
		description,
		startAt,
		scenarioId,
	).Scan(&id)

	if err != nil {
		return 0, err
	}
	return id, nil
}

func (r *GamesRepo) GetGameByID(
	ctx context.Context,
	id int,
) (*repos.Game, error) {
	game := &repos.Game{}
	row := r.pool.QueryRow(
		ctx,
		`SELECT 
			games.id, 
			games.name, 
			games.description, 
			games.start_at,
			games.status,
			games.scenario_id,
			games.started_at,
			games.ended_at
		FROM games
		WHERE id = $1`,
		id,
	)
	err := row.Scan(
		&game.ID,
		&game.Name,
		&game.Description,
		&game.StartAt,
		&game.Status,
		&game.ScenarioId,
		&game.StartedAt,
		&game.EndedAt,
	)
	if err != nil {
		if errors.Is(err, pgx.ErrNoRows) {
			return nil, ErrGameNotFound
		}
		return nil, err
	}

	return game, nil
}

func (r *GamesRepo) DeleteGameByID(
	ctx context.Context,
	id int,
) error {
	tag, err := r.pool.Exec(
		ctx,
		`DELETE FROM games
		WHERE id = $1`,
		id,
	)
	if err != nil {
		return err
	}
	if tag.RowsAffected() == 0 {
		return ErrGameNotFound
	}
	return nil
}

func (r *GamesRepo) GetGames(
	ctx context.Context,
) ([]*repos.Game, error) {
	rows, err := r.pool.Query(
		ctx,
		`SELECT 
			games.id, 
			games.name, 
			games.description, 
			games.start_at,
			games.status
		FROM games
		ORDER BY created_at DESC`,
	)
	if err != nil {
		return nil, err
	}
	defer rows.Close()

	var games []*repos.Game
	for rows.Next() {
		game := &repos.Game{}
		err := rows.Scan(
			&game.ID,
			&game.Name,
			&game.Description,
			&game.StartAt,
			&game.Status,
		)
		if err != nil {
			return nil, err
		}
		games = append(games, game)
	}
	if err := rows.Err(); err != nil {
		return nil, err
	}

	return games, nil
}

func (r *GamesRepo) UpdateGame(
	ctx context.Context,
	id int,
	name string,
	description string,
	startAt time.Time,
	scenarioId int32,
) error {
	tag, err := r.pool.Exec(
		ctx,
		`UPDATE games
		SET 
			name = $1,
			description = $2,
			start_at = $3,
			scenario_id = $4,
			updated_at = NOW()
		WHERE id = $5`,
		name,
		description,
		startAt,
		scenarioId,
		id,
	)
	if err != nil {
		return err
	}
	if tag.RowsAffected() == 0 {
		return ErrGameNotFound
	}
	return nil
}

func (r *GamesRepo) StartGame(ctx context.Context, id int) error {
	tag, err := r.pool.Exec(
		ctx,
		`UPDATE games
		SET 
			status = 'run',
			started_at = NOW(),
			updated_at = NOW()
		WHERE id = $1`,
		id,
	)
	if err != nil {
		return err
	}
	if tag.RowsAffected() == 0 {
		return ErrGameNotFound
	}
	return nil
}

func (r *GamesRepo) PauseGame(ctx context.Context, id int) error {
	tag, err := r.pool.Exec(
		ctx,
		`UPDATE games
		SET 
			status = 'pause',
			updated_at = NOW()
		WHERE id = $1`,
		id,
	)
	if err != nil {
		return err
	}
	if tag.RowsAffected() == 0 {
		return ErrGameNotFound
	}
	return nil
}

func (r *GamesRepo) PlayGame(ctx context.Context, id int) error {
	tag, err := r.pool.Exec(
		ctx,
		`UPDATE games
		SET 
			status = 'run',
			updated_at = NOW()
		WHERE id = $1`,
		id,
	)
	if err != nil {
		return err
	}
	if tag.RowsAffected() == 0 {
		return ErrGameNotFound
	}
	return nil
}

func (r *GamesRepo) FinishGame(ctx context.Context, id int) error {
	tag, err := r.pool.Exec(
		ctx,
		`UPDATE games
		SET 
			status = 'finish',
			ended_at = NOW(),
			updated_at = NOW()
		WHERE id = $1`,
		id,
	)
	if err != nil {
		return err
	}
	if tag.RowsAffected() == 0 {
		return ErrGameNotFound
	}
	return nil
}

func (r *GamesRepo) ResetGame(ctx context.Context, id int) error {
	tag, err := r.pool.Exec(
		ctx,
		`UPDATE games
		SET 
			status = 'draft',
			started_at = DEFAULT,
			ended_at = DEFAULT,
			updated_at = NOW()
		WHERE id = $1`,
		id,
	)
	if err != nil {
		return err
	}
	if tag.RowsAffected() == 0 {
		return ErrGameNotFound
	}
	return nil
}
