package main

import (
	"bytes"
	"context"
	"database/sql"
	"encoding/json"
	"fmt"
	"os/exec"
	"strings"
	"time"
)

// DailySummary persisted row.
type DailySummary struct {
	Date        string  `json:"date"`
	Summary     string  `json:"summary"`
	Prompt      string  `json:"prompt"`
	Model       string  `json:"model"`
	GeneratedAt string  `json:"generated_at"`
	GeneratedBy *string `json:"generated_by"`
}

func (db *DB) GetDailySummary(date string) (*DailySummary, error) {
	row := db.conn.QueryRow(`SELECT date, summary, prompt, model, generated_at, generated_by FROM daily_summaries WHERE date=?`, date)
	var s DailySummary
	var by sql.NullString
	if err := row.Scan(&s.Date, &s.Summary, &s.Prompt, &s.Model, &s.GeneratedAt, &by); err != nil {
		if err == sql.ErrNoRows {
			return nil, nil
		}
		return nil, err
	}
	if by.Valid {
		v := by.String
		s.GeneratedBy = &v
	}
	return &s, nil
}

func (db *DB) UpsertDailySummary(s DailySummary) error {
	var by any = nil
	if s.GeneratedBy != nil {
		by = *s.GeneratedBy
	}
	_, err := db.conn.Exec(`
		INSERT INTO daily_summaries (date, summary, prompt, model, generated_at, generated_by)
		VALUES (?,?,?,?,?,?)
		ON CONFLICT(date) DO UPDATE SET
		  summary=excluded.summary,
		  prompt=excluded.prompt,
		  model=excluded.model,
		  generated_at=excluded.generated_at,
		  generated_by=excluded.generated_by
	`, s.Date, s.Summary, s.Prompt, s.Model, s.GeneratedAt, by)
	return err
}

func (db *DB) GetSetting(key string) (string, error) {
	var v string
	err := db.conn.QueryRow(`SELECT value FROM settings WHERE key=?`, key).Scan(&v)
	if err == sql.ErrNoRows {
		return "", nil
	}
	return v, err
}

func (db *DB) SetSetting(key, value string, by *string) error {
	now := nowRFC3339()
	var byArg any = nil
	if by != nil {
		byArg = *by
	}
	_, err := db.conn.Exec(`
		INSERT INTO settings (key, value, updated_at, updated_by) VALUES (?,?,?,?)
		ON CONFLICT(key) DO UPDATE SET value=excluded.value, updated_at=excluded.updated_at, updated_by=excluded.updated_by
	`, key, value, now, byArg)
	return err
}

// runClaudePrompt executes `claude -p` with the given user prompt; returns
// stdout trimmed. Times out via claudeTimeout from chat.go.
func runClaudePrompt(ctx context.Context, prompt string) (string, error) {
	ctx, cancel := context.WithTimeout(ctx, claudeTimeout)
	defer cancel()
	cmd := exec.CommandContext(ctx, claudeBinary(), "-p", "--model", claudeModel())
	cmd.Stdin = strings.NewReader(prompt)
	var out, errb bytes.Buffer
	cmd.Stdout = &out
	cmd.Stderr = &errb
	if err := cmd.Run(); err != nil {
		return "", fmt.Errorf("claude -p failed: %v: %s", err, strings.TrimSpace(errb.String()))
	}
	return strings.TrimSpace(out.String()), nil
}

// BuildDailySummaryPrompt composes the prompt for the LLM by interpolating the
// configurable instruction template with the JSON of the report.
func BuildDailySummaryPrompt(template string, report *DailyReport) (string, error) {
	js, err := json.MarshalIndent(report, "", "  ")
	if err != nil {
		return "", err
	}
	return fmt.Sprintf("%s\n\n<reporte_json>\n%s\n</reporte_json>\n", template, string(js)), nil
}

// GenerateDailySummary builds the prompt, calls Claude, persists and returns
// the resulting summary. actorID is optional (empty = anon/system).
func (db *DB) GenerateDailySummary(ctx context.Context, date, tz, actorID string) (*DailySummary, error) {
	rep, err := db.DailyReportFor(date, tz)
	if err != nil {
		return nil, err
	}
	tmpl, err := db.GetSetting("daily_report_prompt")
	if err != nil {
		return nil, err
	}
	if tmpl == "" {
		tmpl = "Resume el reporte diario en 4 frases cortas, en castellano, sin inventar datos."
	}
	prompt, err := BuildDailySummaryPrompt(tmpl, rep)
	if err != nil {
		return nil, err
	}
	summary, err := runClaudePrompt(ctx, prompt)
	if err != nil {
		return nil, err
	}
	rec := DailySummary{
		Date:        date,
		Summary:     summary,
		Prompt:      tmpl,
		Model:       claudeModel(),
		GeneratedAt: time.Now().UTC().Format(time.RFC3339Nano),
	}
	if actorID != "" {
		rec.GeneratedBy = &actorID
	}
	if err := db.UpsertDailySummary(rec); err != nil {
		return nil, err
	}
	return &rec, nil
}
