package database

import (
	"context"
	"database/sql"
	"fmt"

	"Engine/internal/model"
)

type ActualsRepo struct {
	db *sql.DB
}

func NewActualsRepo(db *sql.DB) *ActualsRepo {
	return &ActualsRepo{db: db}
}

func (r *ActualsRepo) Ingest(ctx context.Context, req model.IngestActualsRequest) (*model.Actual, error) {
	var deptID, glID int
	if err := r.db.QueryRowContext(ctx, `SELECT id FROM departments WHERE code=?`, req.DeptCode).Scan(&deptID); err != nil {
		return nil, fmt.Errorf("department %q not found: %w", req.DeptCode, err)
	}
	if err := r.db.QueryRowContext(ctx, `SELECT id FROM gl_accounts WHERE code=?`, req.GLCode).Scan(&glID); err != nil {
		return nil, fmt.Errorf("GL account %q not found: %w", req.GLCode, err)
	}

	_, err := r.db.ExecContext(ctx, `
		INSERT INTO actuals (fiscal_year, fiscal_period, department_id, gl_account_id, amount, currency, source)
		VALUES (?,?,?,?,?,?,?)
		ON CONFLICT (fiscal_year, fiscal_period, department_id, gl_account_id)
		DO UPDATE SET amount=excluded.amount, source=excluded.source`,
		req.FiscalYear, req.FiscalPeriod, deptID, glID,
		req.Amount, req.Currency, req.Source,
	)
	if err != nil {
		return nil, fmt.Errorf("upsert actual: %w", err)
	}

	a := &model.Actual{}
	err = r.db.QueryRowContext(ctx, `
		SELECT id, fiscal_year, fiscal_period, department_id, gl_account_id,
		       amount, currency, source, ingested_at
		FROM actuals
		WHERE fiscal_year=? AND fiscal_period=? AND department_id=? AND gl_account_id=?`,
		req.FiscalYear, req.FiscalPeriod, deptID, glID,
	).Scan(
		&a.ID, &a.FiscalYear, &a.FiscalPeriod,
		&a.DepartmentID, &a.GLAccountID,
		&a.Amount, &a.Currency, &a.Source, &a.IngestedAt,
	)
	if err != nil {
		return nil, fmt.Errorf("fetch ingested actual: %w", err)
	}
	return a, nil
}

func (r *ActualsRepo) ListByPeriod(ctx context.Context, fiscalYear, fiscalPeriod int, deptCode string) (map[string]float64, error) {
	rows, err := r.db.QueryContext(ctx, `
		SELECT g.code, a.amount
		FROM actuals a
		JOIN departments d ON d.id = a.department_id
		JOIN gl_accounts g ON g.id = a.gl_account_id
		WHERE a.fiscal_year   = ?
		  AND a.fiscal_period = ?
		  AND (? = '' OR d.code = ?)`,
		fiscalYear, fiscalPeriod, deptCode, deptCode,
	)
	if err != nil {
		return nil, fmt.Errorf("list actuals: %w", err)
	}
	defer rows.Close()

	result := make(map[string]float64)
	for rows.Next() {
		var code string
		var amount float64
		if err := rows.Scan(&code, &amount); err != nil {
			return nil, err
		}
		result[code] += amount
	}
	return result, rows.Err()
}
