package storage

import (
	"context"
	"errors"
	"git.sch9.ru/new_gate/ms-tester/internal/models"
	"github.com/jmoiron/sqlx"
	"go.uber.org/zap"
)

type ProblemStorage struct {
	db     *sqlx.DB
	logger *zap.Logger
}

func NewProblemStorage(db *sqlx.DB, logger *zap.Logger) *ProblemStorage {
	return &ProblemStorage{
		db:     db,
		logger: logger,
	}
}

func (storage *ProblemStorage) CreateProblem(ctx context.Context, problem *models.Problem, testGroupData []models.TestGroupData) (int32, error) {
	tx, err := storage.db.Beginx()
	if err != nil {
		return 0, handlePgErr(err)
	}
	query := tx.Rebind(`
INSERT INTO problems
    (name,description,time_limit,memory_limit)
VALUES (?, ?, ?, ?)
RETURNING id
`)
	rows, err := tx.QueryxContext(
		ctx,
		query,
		problem.Name,
		problem.Description,
		problem.TimeLimit,
		problem.MemoryLimit,
	)
	if err != nil {
		return 0, handlePgErr(errors.Join(err, tx.Rollback()))
	}
	for _, tgd := range testGroupData {
		query := tx.Rebind(`
    INSERT INTO testgroups
        (problem_id,testing_strategy)
    VALUES ((select last_value from problems_id_seq),?)
    RETURNING id
    `)
		rows, err = tx.QueryxContext(ctx, query, tgd.Ts)
		if err != nil {
			return 0, handlePgErr(errors.Join(err, tx.Rollback()))
		}
		var i int32 = 0
		for ; i < tgd.TestAmount; i++ {
			query := tx.Rebind(`
        INSERT INTO tests
            (testgroup_id)
        VALUES ((select last_value from testgroups_id_seq))
        RETURNING id
        `)
			rows, err = tx.QueryxContext(ctx, query, tgd.Ts)
			if err != nil {
				return 0, handlePgErr(errors.Join(err, tx.Rollback()))
			}
		}
	}
	err = tx.Commit()
	//add test saving

	defer rows.Close()
	var id int32
	err = rows.StructScan(&id)
	if err != nil {
		return 0, handlePgErr(err)
	}

	return id, nil
}

func (storage *ProblemStorage) ReadProblemById(ctx context.Context, id int32) (*models.Problem, error) {
	var problem models.Problem
	query := storage.db.Rebind("SELECT * from problems WHERE id=? LIMIT 1")
	err := storage.db.GetContext(ctx, &problem, query, id)
	if err != nil {
		return nil, handlePgErr(err)
	}
	return &problem, nil
}

func (storage *ProblemStorage) UpdateProblem(ctx context.Context, problem *models.Problem) error {
	query := storage.db.Rebind("UPDATE problems SET name=?,description=?,time_limit=?,memory_limit=? WHERE id=?")
	_, err := storage.db.ExecContext(ctx, query, problem.Name, problem.Description, problem.TimeLimit, problem.MemoryLimit, problem.Id)
	if err != nil {
		return handlePgErr(err)
	}
	return nil
}

func (storage *ProblemStorage) DeleteProblem(ctx context.Context, id int32) error {
	query := storage.db.Rebind("DELETE FROM problems WHERE id=?")
	_, err := storage.db.ExecContext(ctx, query, id)
	if err != nil {
		return handlePgErr(err)
	}

	return nil
}
