package queries

import (
	"context"
	"database/sql"
	"fmt"
	"strings"
	"time"
)

type ProductionCostingLast10WarningRow struct {
	NOnMLNo         int     `json:"n_onml_no"`
	UrunKodu        string  `json:"urun_kodu"`
	MaliyetTarihi   string  `json:"maliyet_tarihi"` // YYYY-MM-DD
	ItemCode        string  `json:"item_code"`
	ItemDescription string  `json:"item_description,omitempty"`
	CurrencyCode    string  `json:"currency_code"`
	InputPrice      float64 `json:"input_price"`
	AvgDocPrice     float64 `json:"avg_doc_price"`
	InputUSD        float64 `json:"input_usd"`
	AvgUSD          float64 `json:"avg_usd"`
	DiffRatio       float64 `json:"diff_ratio"` // e.g. 0.12 means 12%
	SampleCount     int     `json:"sample_count"`
	MinInvoice      string  `json:"min_invoice_date,omitempty"`
	MaxInvoice      string  `json:"max_invoice_date,omitempty"`
	CreatedAt       string  `json:"created_at,omitempty"`
	CreatedBy       string  `json:"created_by,omitempty"`
}

func EnsureProductionCostingLast10WarningTables(pg *sql.DB) error {
	if pg == nil {
		return fmt.Errorf("pg db is nil")
	}
	stmts := []string{
		`
CREATE TABLE IF NOT EXISTS mk_costing_last10_warning ( 
  n_onml_no INT NOT NULL, 
  item_code TEXT NOT NULL, 
  currency_code TEXT NOT NULL, 
  urun_kodu TEXT NOT NULL DEFAULT '', 
  item_description TEXT NOT NULL DEFAULT '', 
  maliyet_tarihi DATE, 
  input_price DOUBLE PRECISION NOT NULL DEFAULT 0, 
  avg_doc_price DOUBLE PRECISION NOT NULL DEFAULT 0, 
  input_usd DOUBLE PRECISION NOT NULL DEFAULT 0, 
  avg_usd DOUBLE PRECISION NOT NULL DEFAULT 0, 
  diff_ratio DOUBLE PRECISION NOT NULL DEFAULT 0,
  sample_count INT NOT NULL DEFAULT 0,
  min_invoice_date DATE,
  max_invoice_date DATE,
  created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
  created_by TEXT NOT NULL DEFAULT '', 
  PRIMARY KEY (n_onml_no, item_code, currency_code) 
) 
`,
		// Best-effort forward migration for existing DBs.
		`ALTER TABLE mk_costing_last10_warning ADD COLUMN IF NOT EXISTS item_description TEXT NOT NULL DEFAULT ''`,
		`CREATE INDEX IF NOT EXISTS ix_costing_last10_warning_onml ON mk_costing_last10_warning (n_onml_no)`,
		`CREATE INDEX IF NOT EXISTS ix_costing_last10_warning_item ON mk_costing_last10_warning (item_code, currency_code)`,
	}
	for _, s := range stmts {
		if _, err := pg.Exec(s); err != nil {
			return err
		}
	}
	return nil
}

func ReplaceProductionCostingLast10Warnings(
	ctx context.Context,
	pg *sql.DB,
	nOnMLNo int,
	urunKodu string,
	maliyetTarihi string, // YYYY-MM-DD
	createdBy string,
	items []Last10AvgPurchasePriceRow,
	inputByKey map[string]float64, // key = ITEM|CUR  (input in doc currency)
	inputUSDByKey map[string]float64, // key = ITEM|CUR (input converted to USD basis)
	avgUSDByKey map[string]float64, // key = ITEM|CUR (avg converted to USD basis)
	descByCode map[string]string, // code -> description (UI text)
) error {
	if pg == nil {
		return fmt.Errorf("pg db is nil")
	}
	if nOnMLNo <= 0 {
		return fmt.Errorf("invalid n_onml_no")
	}
	urunKodu = strings.TrimSpace(urunKodu)
	createdBy = strings.TrimSpace(createdBy)
	maliyetTarihi = strings.TrimSpace(maliyetTarihi)

	// best-effort date parse for insert; allow NULL date if invalid
	var mtDate any = nil
	if maliyetTarihi != "" {
		if t, err := time.Parse("2006-01-02", maliyetTarihi); err == nil {
			mtDate = t
		}
	}

	tx, err := pg.BeginTx(ctx, nil)
	if err != nil {
		return err
	}
	defer func() { _ = tx.Rollback() }()

	if _, err := tx.ExecContext(ctx, `DELETE FROM mk_costing_last10_warning WHERE n_onml_no = $1`, nOnMLNo); err != nil {
		return err
	}

	// insert only rows that exist in both cache and inputByKey and violate threshold
	for _, ar := range items {
		code := strings.TrimSpace(ar.ItemCode)
		cur := strings.ToUpper(strings.TrimSpace(ar.CurrencyCode))
		if code == "" || cur == "" {
			continue
		}
		key := code + "|" + cur
		in := inputByKey[key]
		inUSD := inputUSDByKey[key]
		avgUSD := avgUSDByKey[key]
		if in <= 0 || ar.SampleCount <= 0 || ar.AvgDocPrice <= 0 {
			continue
		}
		// diff ratio is evaluated on USD basis (requested behavior)
		if inUSD <= 0 || avgUSD <= 0 {
			continue
		}
		diff := (inUSD - avgUSD) / avgUSD
		if diff < 0 {
			diff = -diff
		}
		if diff <= 0.10 {
			continue
		}

		desc := strings.TrimSpace(descByCode[code])

		_, err := tx.ExecContext(ctx, ` 
INSERT INTO mk_costing_last10_warning ( 
  n_onml_no, item_code, currency_code, 
  urun_kodu, item_description, maliyet_tarihi, 
  input_price, avg_doc_price, input_usd, avg_usd, diff_ratio, 
  sample_count, min_invoice_date, max_invoice_date, 
  created_by 
) VALUES ( 
  $1,$2,$3, 
  $4,$5,$6, 
  $7,$8,$9,$10,$11, 
  $12,$13,$14, 
  $15 
) 
ON CONFLICT (n_onml_no, item_code, currency_code) DO UPDATE SET 
  urun_kodu = EXCLUDED.urun_kodu, 
  item_description = EXCLUDED.item_description, 
  maliyet_tarihi = EXCLUDED.maliyet_tarihi, 
  input_price = EXCLUDED.input_price, 
  avg_doc_price = EXCLUDED.avg_doc_price, 
  input_usd = EXCLUDED.input_usd, 
  avg_usd = EXCLUDED.avg_usd, 
  diff_ratio = EXCLUDED.diff_ratio,
  sample_count = EXCLUDED.sample_count,
  min_invoice_date = EXCLUDED.min_invoice_date,
  max_invoice_date = EXCLUDED.max_invoice_date,
  created_at = now(), 
  created_by = EXCLUDED.created_by 
`, nOnMLNo, code, cur, urunKodu, desc, mtDate, in, ar.AvgDocPrice, inUSD, avgUSD, diff, ar.SampleCount, ar.MinInvoiceDate, ar.MaxInvoiceDate, createdBy)
		if err != nil {
			return err
		}
	}

	if err := tx.Commit(); err != nil {
		return err
	}
	return nil
}

func ListProductionCostingLast10WarningsByOnMLNo(ctx context.Context, pg *sql.DB, nOnMLNo int) ([]ProductionCostingLast10WarningRow, error) {
	if pg == nil {
		return nil, fmt.Errorf("pg db is nil")
	}
	rows, err := pg.QueryContext(ctx, `
SELECT 
  n_onml_no, 
  COALESCE(urun_kodu,'') AS urun_kodu, 
  COALESCE(TO_CHAR(maliyet_tarihi, 'YYYY-MM-DD'),'') AS maliyet_tarihi, 
  item_code, 
  COALESCE(item_description,'') AS item_description, 
  currency_code, 
  input_price, 
  avg_doc_price, 
  input_usd, 
  avg_usd, 
  diff_ratio,
  sample_count,
  COALESCE(TO_CHAR(min_invoice_date, 'YYYY-MM-DD'),'') AS min_invoice_date,
  COALESCE(TO_CHAR(max_invoice_date, 'YYYY-MM-DD'),'') AS max_invoice_date,
  COALESCE(TO_CHAR(created_at, 'YYYY-MM-DD HH24:MI:SS'),'') AS created_at,
  COALESCE(created_by,'') AS created_by
FROM mk_costing_last10_warning
WHERE n_onml_no = $1
ORDER BY diff_ratio DESC, item_code ASC, currency_code ASC
`, nOnMLNo)
	if err != nil {
		return nil, err
	}
	defer rows.Close()

	out := make([]ProductionCostingLast10WarningRow, 0, 32)
	for rows.Next() {
		var r ProductionCostingLast10WarningRow
		if err := rows.Scan(
			&r.NOnMLNo,
			&r.UrunKodu,
			&r.MaliyetTarihi,
			&r.ItemCode,
			&r.ItemDescription,
			&r.CurrencyCode,
			&r.InputPrice,
			&r.AvgDocPrice,
			&r.InputUSD,
			&r.AvgUSD,
			&r.DiffRatio,
			&r.SampleCount,
			&r.MinInvoice,
			&r.MaxInvoice,
			&r.CreatedAt,
			&r.CreatedBy,
		); err != nil {
			return nil, err
		}
		out = append(out, r)
	}
	if err := rows.Err(); err != nil {
		return nil, err
	}
	return out, nil
}
