package main

import (
	"encoding/json"
	"fmt"
	"os"
	"strings"
	"text/tabwriter"
	"time"

	"fn-registry/registry"
)

func cmdProposal(args []string) {
	if len(args) < 1 {
		printProposalUsage()
		os.Exit(1)
	}

	switch args[0] {
	case "add":
		cmdProposalAdd(args[1:])
	case "list":
		cmdProposalList(args[1:])
	case "show":
		cmdProposalShow(args[1:])
	case "update":
		cmdProposalUpdate(args[1:])
	case "help", "-h", "--help":
		printProposalUsage()
	default:
		fmt.Fprintf(os.Stderr, "unknown proposal command: %s\n", args[0])
		printProposalUsage()
		os.Exit(1)
	}
}

func printProposalUsage() {
	fmt.Println(`fn proposal — gestiona proposals

Usage:
  fn proposal add    --kind <kind> --title <title> [options]
  fn proposal list   [-k kind] [-s status]
  fn proposal show   <id>
  fn proposal update <id> --status <status> [--reviewed-by <who>]

Kinds: new_function, new_type, improve_function, improve_type, new_pipeline
Status: pending, approved, rejected, implemented`)
}

func cmdProposalAdd(args []string) {
	var id, kind, targetID, title, description, evidenceStr, createdBy string
	i := 0
	for i < len(args) {
		switch args[i] {
		case "--id":
			i++
			id = args[i]
		case "--kind":
			i++
			kind = args[i]
		case "--target-id":
			i++
			targetID = args[i]
		case "--title":
			i++
			title = args[i]
		case "--description":
			i++
			description = args[i]
		case "--evidence":
			i++
			evidenceStr = args[i]
		case "--created-by":
			i++
			createdBy = args[i]
		}
		i++
	}

	if kind == "" || title == "" {
		fmt.Fprintln(os.Stderr, "error: --kind and --title are required")
		os.Exit(1)
	}

	if id == "" {
		id = fmt.Sprintf("proposal_%d", time.Now().UnixNano())
	}

	var evidence map[string]any
	if evidenceStr != "" {
		if err := json.Unmarshal([]byte(evidenceStr), &evidence); err != nil {
			fmt.Fprintf(os.Stderr, "error: invalid evidence JSON: %v\n", err)
			os.Exit(1)
		}
	}

	p := &registry.Proposal{
		ID:          id,
		Kind:        registry.ProposalKind(kind),
		TargetID:    targetID,
		Title:       title,
		Description: description,
		Evidence:    evidence,
		Status:      registry.ProposalPending,
		CreatedBy:   createdBy,
	}

	if err := registry.ValidateProposal(p); err != nil {
		fmt.Fprintf(os.Stderr, "error: %v\n", err)
		os.Exit(1)
	}

	db := openDB()
	defer db.Close()

	if err := db.InsertProposal(p); err != nil {
		fmt.Fprintf(os.Stderr, "error: %v\n", err)
		os.Exit(1)
	}

	fmt.Printf("Created proposal: %s\n", p.ID)
}

func cmdProposalList(args []string) {
	var kind, status string
	i := 0
	for i < len(args) {
		switch args[i] {
		case "-k":
			i++
			kind = args[i]
		case "-s":
			i++
			status = args[i]
		}
		i++
	}

	db := openDB()
	defer db.Close()

	proposals, err := db.ListProposals(registry.ProposalKind(kind), registry.ProposalStatus(status))
	if err != nil {
		fmt.Fprintf(os.Stderr, "error: %v\n", err)
		os.Exit(1)
	}

	if len(proposals) == 0 {
		fmt.Println("No proposals.")
		return
	}

	w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0)
	fmt.Fprintln(w, "ID\tKIND\tSTATUS\tTITLE\tCREATED_BY")
	for _, p := range proposals {
		fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\n", p.ID, p.Kind, p.Status, truncate(p.Title, 40), p.CreatedBy)
	}
	w.Flush()
}

func cmdProposalShow(args []string) {
	if len(args) < 1 {
		fmt.Fprintln(os.Stderr, "usage: fn proposal show <id>")
		os.Exit(1)
	}

	db := openDB()
	defer db.Close()

	p, err := db.GetProposal(args[0])
	if err != nil {
		fmt.Fprintf(os.Stderr, "error: %v\n", err)
		os.Exit(1)
	}

	fmt.Printf("ID:          %s\n", p.ID)
	fmt.Printf("Kind:        %s\n", p.Kind)
	fmt.Printf("Status:      %s\n", p.Status)
	fmt.Printf("Title:       %s\n", p.Title)
	fmt.Printf("Description: %s\n", p.Description)
	if p.TargetID != "" {
		fmt.Printf("Target ID:   %s\n", p.TargetID)
	}
	if len(p.Evidence) > 0 {
		ev, _ := json.MarshalIndent(p.Evidence, "             ", "  ")
		fmt.Printf("Evidence:    %s\n", string(ev))
	}
	fmt.Printf("Created by:  %s\n", p.CreatedBy)
	if p.ReviewedBy != "" {
		fmt.Printf("Reviewed by: %s\n", p.ReviewedBy)
	}
	fmt.Printf("Created:     %s\n", p.CreatedAt.Format(time.RFC3339))
	fmt.Printf("Updated:     %s\n", p.UpdatedAt.Format(time.RFC3339))
}

func cmdProposalUpdate(args []string) {
	if len(args) < 1 {
		fmt.Fprintln(os.Stderr, "usage: fn proposal update <id> --status <status> [--reviewed-by <who>]")
		os.Exit(1)
	}

	id := args[0]
	var status, reviewedBy string
	i := 1
	for i < len(args) {
		switch args[i] {
		case "--status":
			i++
			status = args[i]
		case "--reviewed-by":
			i++
			reviewedBy = args[i]
		}
		i++
	}

	db := openDB()
	defer db.Close()

	p, err := db.GetProposal(id)
	if err != nil {
		fmt.Fprintf(os.Stderr, "error: %v\n", err)
		os.Exit(1)
	}

	if status != "" {
		p.Status = registry.ProposalStatus(status)
	}
	if reviewedBy != "" {
		p.ReviewedBy = reviewedBy
	}

	// Validate updated proposal
	validKinds := map[string]bool{
		"pending": true, "approved": true, "rejected": true, "implemented": true,
	}
	if status != "" && !validKinds[status] {
		fmt.Fprintf(os.Stderr, "error: invalid status %q\n", status)
		os.Exit(1)
	}

	if err := db.UpdateProposal(p); err != nil {
		fmt.Fprintf(os.Stderr, "error: %v\n", err)
		os.Exit(1)
	}

	fmt.Printf("Updated proposal: %s (status: %s)\n", p.ID, p.Status)
}

func formatEvidence(evidence map[string]any) string {
	if len(evidence) == 0 {
		return "{}"
	}
	b, _ := json.MarshalIndent(evidence, "", "  ")
	return string(b)
}

// formatStrings joins a slice for display, handling nil/empty.
func formatStrings(ss []string) string {
	if len(ss) == 0 {
		return ""
	}
	return strings.Join(ss, ", ")
}
