package iace

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

	"github.com/google/uuid"
	"github.com/jackc/pgx/v5"
)

// ClarificationRow is the persisted shape (one row per [project, clarification_key]).
type ClarificationRow struct {
	ID               uuid.UUID
	TenantID         uuid.UUID
	ProjectID        uuid.UUID
	ClarificationKey string
	Question         string
	Source           string
	Category         string
	NormReferences   []string
	Status           string
	Answer           string
	Reasoning        string
	AssignedTo       string
	AnsweredBy       string
	AnsweredAt       *time.Time
	CreatedAt        time.Time
	UpdatedAt        time.Time
}

// ClarificationComment is a single comment on a clarification.
type ClarificationComment struct {
	ID              uuid.UUID
	ClarificationID uuid.UUID
	Author          string
	Body            string
	CreatedAt       time.Time
}

// ClarificationHistoryEntry logs status/answer transitions.
type ClarificationHistoryEntry struct {
	ID              uuid.UUID
	ClarificationID uuid.UUID
	Actor           string
	FromStatus      string
	ToStatus        string
	FromAnswer      string
	ToAnswer        string
	Note            string
	CreatedAt       time.Time
}

// UpsertClarification creates or updates a clarification row by
// (project_id, clarification_key) and logs the status/answer transition
// in iace_clarification_history when something changes.
func (s *Store) UpsertClarification(ctx context.Context, in ClarificationRow) (*ClarificationRow, error) {
	tx, err := s.pool.Begin(ctx)
	if err != nil {
		return nil, fmt.Errorf("begin upsert clarification: %w", err)
	}
	defer func() { _ = tx.Rollback(ctx) }()

	// Fetch existing for history diff
	var prev ClarificationRow
	prevErr := tx.QueryRow(ctx, `
		SELECT id, status, answer
		FROM iace_clarifications
		WHERE project_id = $1 AND clarification_key = $2
	`, in.ProjectID, in.ClarificationKey).Scan(&prev.ID, &prev.Status, &prev.Answer)
	hadPrev := prevErr == nil
	if prevErr != nil && !errors.Is(prevErr, pgx.ErrNoRows) && !errors.Is(prevErr, sql.ErrNoRows) {
		return nil, fmt.Errorf("lookup existing clarification: %w", prevErr)
	}

	var row ClarificationRow
	err = tx.QueryRow(ctx, `
		INSERT INTO iace_clarifications (
			tenant_id, project_id, clarification_key, question, source, category,
			norm_references, status, answer, reasoning, assigned_to, answered_by, answered_at
		) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13)
		ON CONFLICT (project_id, clarification_key) DO UPDATE SET
			question = EXCLUDED.question,
			source = EXCLUDED.source,
			category = EXCLUDED.category,
			norm_references = EXCLUDED.norm_references,
			status = EXCLUDED.status,
			answer = EXCLUDED.answer,
			reasoning = EXCLUDED.reasoning,
			assigned_to = EXCLUDED.assigned_to,
			answered_by = EXCLUDED.answered_by,
			answered_at = EXCLUDED.answered_at
		RETURNING id, tenant_id, project_id, clarification_key, question, source, category,
			norm_references, status, answer, reasoning, assigned_to, answered_by, answered_at,
			created_at, updated_at
	`,
		in.TenantID, in.ProjectID, in.ClarificationKey, in.Question, in.Source, in.Category,
		in.NormReferences, in.Status, in.Answer, in.Reasoning, in.AssignedTo, in.AnsweredBy, in.AnsweredAt,
	).Scan(
		&row.ID, &row.TenantID, &row.ProjectID, &row.ClarificationKey, &row.Question, &row.Source, &row.Category,
		&row.NormReferences, &row.Status, &row.Answer, &row.Reasoning, &row.AssignedTo, &row.AnsweredBy, &row.AnsweredAt,
		&row.CreatedAt, &row.UpdatedAt,
	)
	if err != nil {
		return nil, fmt.Errorf("upsert clarification: %w", err)
	}

	// Log transition iff something changed
	if hadPrev && (prev.Status != row.Status || prev.Answer != row.Answer) {
		_, err = tx.Exec(ctx, `
			INSERT INTO iace_clarification_history (clarification_id, actor, from_status, to_status, from_answer, to_answer, note)
			VALUES ($1, $2, $3, $4, $5, $6, $7)
		`, row.ID, in.AnsweredBy, prev.Status, row.Status, prev.Answer, row.Answer, "")
		if err != nil {
			return nil, fmt.Errorf("write history: %w", err)
		}
	}
	if err := tx.Commit(ctx); err != nil {
		return nil, fmt.Errorf("commit upsert clarification: %w", err)
	}
	return &row, nil
}

// ListClarificationsForProject returns all clarification rows for a project.
func (s *Store) ListClarificationsForProject(ctx context.Context, projectID uuid.UUID) ([]ClarificationRow, error) {
	rows, err := s.pool.Query(ctx, `
		SELECT id, tenant_id, project_id, clarification_key, question, source, category,
			norm_references, status, answer, reasoning, assigned_to, answered_by, answered_at,
			created_at, updated_at
		FROM iace_clarifications
		WHERE project_id = $1
		ORDER BY status, source, created_at
	`, projectID)
	if err != nil {
		return nil, fmt.Errorf("list clarifications: %w", err)
	}
	defer rows.Close()
	out := []ClarificationRow{}
	for rows.Next() {
		var r ClarificationRow
		if err := rows.Scan(
			&r.ID, &r.TenantID, &r.ProjectID, &r.ClarificationKey, &r.Question, &r.Source, &r.Category,
			&r.NormReferences, &r.Status, &r.Answer, &r.Reasoning, &r.AssignedTo, &r.AnsweredBy, &r.AnsweredAt,
			&r.CreatedAt, &r.UpdatedAt,
		); err != nil {
			return nil, err
		}
		out = append(out, r)
	}
	return out, rows.Err()
}

// GetClarificationByKey fetches a clarification by project + key.
func (s *Store) GetClarificationByKey(ctx context.Context, projectID uuid.UUID, key string) (*ClarificationRow, error) {
	var r ClarificationRow
	err := s.pool.QueryRow(ctx, `
		SELECT id, tenant_id, project_id, clarification_key, question, source, category,
			norm_references, status, answer, reasoning, assigned_to, answered_by, answered_at,
			created_at, updated_at
		FROM iace_clarifications
		WHERE project_id = $1 AND clarification_key = $2
	`, projectID, key).Scan(
		&r.ID, &r.TenantID, &r.ProjectID, &r.ClarificationKey, &r.Question, &r.Source, &r.Category,
		&r.NormReferences, &r.Status, &r.Answer, &r.Reasoning, &r.AssignedTo, &r.AnsweredBy, &r.AnsweredAt,
		&r.CreatedAt, &r.UpdatedAt,
	)
	if errors.Is(err, pgx.ErrNoRows) {
		return nil, nil
	}
	if err != nil {
		return nil, fmt.Errorf("get clarification: %w", err)
	}
	return &r, nil
}

// AddClarificationComment appends a comment to the thread.
func (s *Store) AddClarificationComment(ctx context.Context, clarID uuid.UUID, author, body string) (*ClarificationComment, error) {
	var c ClarificationComment
	err := s.pool.QueryRow(ctx, `
		INSERT INTO iace_clarification_comments (clarification_id, author, body)
		VALUES ($1, $2, $3)
		RETURNING id, clarification_id, author, body, created_at
	`, clarID, author, body).Scan(&c.ID, &c.ClarificationID, &c.Author, &c.Body, &c.CreatedAt)
	if err != nil {
		return nil, fmt.Errorf("add clarification comment: %w", err)
	}
	return &c, nil
}

// ListClarificationComments returns the comment thread, oldest first.
func (s *Store) ListClarificationComments(ctx context.Context, clarID uuid.UUID) ([]ClarificationComment, error) {
	rows, err := s.pool.Query(ctx, `
		SELECT id, clarification_id, author, body, created_at
		FROM iace_clarification_comments
		WHERE clarification_id = $1
		ORDER BY created_at
	`, clarID)
	if err != nil {
		return nil, fmt.Errorf("list clarification comments: %w", err)
	}
	defer rows.Close()
	out := []ClarificationComment{}
	for rows.Next() {
		var c ClarificationComment
		if err := rows.Scan(&c.ID, &c.ClarificationID, &c.Author, &c.Body, &c.CreatedAt); err != nil {
			return nil, err
		}
		out = append(out, c)
	}
	return out, rows.Err()
}

// ListClarificationHistory returns the audit trail entries for a clarification.
func (s *Store) ListClarificationHistory(ctx context.Context, clarID uuid.UUID) ([]ClarificationHistoryEntry, error) {
	rows, err := s.pool.Query(ctx, `
		SELECT id, clarification_id, actor, from_status, to_status, from_answer, to_answer, note, created_at
		FROM iace_clarification_history
		WHERE clarification_id = $1
		ORDER BY created_at DESC
	`, clarID)
	if err != nil {
		return nil, fmt.Errorf("list clarification history: %w", err)
	}
	defer rows.Close()
	out := []ClarificationHistoryEntry{}
	for rows.Next() {
		var h ClarificationHistoryEntry
		var fromStatus, toStatus, fromAnswer, toAnswer sql.NullString
		if err := rows.Scan(&h.ID, &h.ClarificationID, &h.Actor, &fromStatus, &toStatus, &fromAnswer, &toAnswer, &h.Note, &h.CreatedAt); err != nil {
			return nil, err
		}
		h.FromStatus = fromStatus.String
		h.ToStatus = toStatus.String
		h.FromAnswer = fromAnswer.String
		h.ToAnswer = toAnswer.String
		out = append(out, h)
	}
	return out, rows.Err()
}
