package server

import (
	"encoding/json"
	"fmt"
	"net/http"
	"os"
	"path/filepath"
	"strconv"
	"time"

	"github.com/operator/command-center/internal/backup"
	"github.com/operator/command-center/internal/emergency"
)

// PolishDeps wraps the Phase 15 deps (emergency, backup, audit).
type PolishDeps struct {
	Emergency *emergency.Manager
	BackupDir string
	AgeKey    string
}

// Emergency.

func (s *Server) handleEmergencyState(w http.ResponseWriter, r *http.Request) {
	if s.polish == nil || s.polish.Emergency == nil {
		http.Error(w, "emergency not configured", http.StatusServiceUnavailable)
		return
	}
	writeJSON(w, http.StatusOK, s.polish.Emergency.Current())
}

func (s *Server) handleEmergencyActivate(w http.ResponseWriter, r *http.Request) {
	if s.polish == nil || s.polish.Emergency == nil {
		http.Error(w, "emergency not configured", http.StatusServiceUnavailable)
		return
	}
	silenceSec := int64(7200)
	if v := r.URL.Query().Get("silence_seconds"); v != "" {
		if n, _ := strconv.ParseInt(v, 10, 64); n > 0 {
			silenceSec = n
		}
	}
	state, err := s.polish.Emergency.Activate(r.Context(), time.Duration(silenceSec)*time.Second)
	if err != nil {
		http.Error(w, err.Error(), http.StatusConflict)
		return
	}
	writeJSON(w, http.StatusOK, state)
}

func (s *Server) handleEmergencyDeactivate(w http.ResponseWriter, r *http.Request) {
	if s.polish == nil || s.polish.Emergency == nil {
		http.Error(w, "emergency not configured", http.StatusServiceUnavailable)
		return
	}
	state, err := s.polish.Emergency.Deactivate(r.Context())
	if err != nil {
		http.Error(w, err.Error(), http.StatusConflict)
		return
	}
	writeJSON(w, http.StatusOK, state)
}

// Backup.

func (s *Server) handleBackupNow(w http.ResponseWriter, r *http.Request) {
	if s.polish == nil || s.polish.BackupDir == "" || s.sqlite == nil {
		http.Error(w, "backup not configured", http.StatusServiceUnavailable)
		return
	}
	name := backup.FormatTimestampedName("command-center", "db", time.Now())
	dest := filepath.Join(s.polish.BackupDir, name)
	size, err := backup.LocalBackup(r.Context(), s.sqlite, dest)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	encryptedPath := ""
	if s.polish.AgeKey != "" {
		encryptedPath = dest + ".age"
		if err := backup.Encrypt(dest, encryptedPath, s.polish.AgeKey); err != nil {
			s.logger.Warn().Err(err).Msg("backup encrypt failed; keeping plaintext copy")
			encryptedPath = ""
		}
	}
	deleted, _ := backup.Prune(s.polish.BackupDir, "command-center-*.db", 30)
	writeJSON(w, http.StatusOK, map[string]any{
		"path":           dest,
		"encrypted_path": encryptedPath,
		"size_bytes":     size,
		"pruned":         deleted,
	})
}

// Audit log.

func (s *Server) handleAuditLog(w http.ResponseWriter, r *http.Request) {
	if s.sqlite == nil {
		http.Error(w, "sqlite required", http.StatusServiceUnavailable)
		return
	}
	limit := 200
	if v := r.URL.Query().Get("limit"); v != "" {
		if n, _ := strconv.Atoi(v); n > 0 && n <= 1000 {
			limit = n
		}
	}
	actor := r.URL.Query().Get("actor")
	action := r.URL.Query().Get("action")
	q := `SELECT id, timestamp, actor, action, target_type, target_id, details_json, ip_address, user_agent
		FROM audit_log`
	args := []any{}
	clauses := []string{}
	if actor != "" {
		clauses = append(clauses, "actor = ?")
		args = append(args, actor)
	}
	if action != "" {
		clauses = append(clauses, "action = ?")
		args = append(args, action)
	}
	for i, c := range clauses {
		if i == 0 {
			q += " WHERE " + c
		} else {
			q += " AND " + c
		}
	}
	q += " ORDER BY timestamp DESC LIMIT ?"
	args = append(args, limit)
	rows, err := s.sqlite.QueryContext(r.Context(), q, args...)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	defer rows.Close()
	type entry struct {
		ID         int64  `json:"id"`
		Timestamp  int64  `json:"timestamp"`
		Actor      string `json:"actor"`
		Action     string `json:"action"`
		TargetType string `json:"target_type,omitempty"`
		TargetID   string `json:"target_id,omitempty"`
		Details    string `json:"details_json,omitempty"`
		IP         string `json:"ip_address,omitempty"`
		UserAgent  string `json:"user_agent,omitempty"`
	}
	out := []entry{}
	for rows.Next() {
		var e entry
		var tType, tID, dets, ip, ua interface{ Scan(any) error }
		_ = tType
		_ = tID
		_ = dets
		_ = ip
		_ = ua
		var t, i, det, ipS, uaS *string
		if err := rows.Scan(&e.ID, &e.Timestamp, &e.Actor, &e.Action, &t, &i, &det, &ipS, &uaS); err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}
		if t != nil {
			e.TargetType = *t
		}
		if i != nil {
			e.TargetID = *i
		}
		if det != nil {
			e.Details = *det
		}
		if ipS != nil {
			e.IP = *ipS
		}
		if uaS != nil {
			e.UserAgent = *uaS
		}
		out = append(out, e)
	}
	writeJSON(w, http.StatusOK, map[string]any{"count": len(out), "entries": out})
}

// Data export.

func (s *Server) handleSystemExport(w http.ResponseWriter, r *http.Request) {
	if s.sqlite == nil {
		http.Error(w, "sqlite required", http.StatusServiceUnavailable)
		return
	}
	table := r.URL.Query().Get("table")
	if table == "" {
		http.Error(w, "table query parameter required", http.StatusBadRequest)
		return
	}
	// Allowlist to prevent arbitrary table read.
	allowed := map[string]bool{
		"audit_log":          true,
		"decision_log":       true,
		"ratio_snapshots":    true,
		"torrent_snapshots":  true,
		"filter_performance": true,
		"system_events":      true,
	}
	if !allowed[table] {
		http.Error(w, "table not allowed", http.StatusBadRequest)
		return
	}
	rows, err := s.sqlite.QueryContext(r.Context(), fmt.Sprintf("SELECT * FROM %s LIMIT 100000", table))
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	defer rows.Close()
	cols, _ := rows.Columns()
	w.Header().Set("Content-Type", "application/json")
	w.Header().Set("Content-Disposition", `attachment; filename="`+table+`.json"`)
	enc := json.NewEncoder(w)
	for rows.Next() {
		vals := make([]any, len(cols))
		ptrs := make([]any, len(cols))
		for i := range vals {
			ptrs[i] = &vals[i]
		}
		if err := rows.Scan(ptrs...); err != nil {
			break
		}
		out := map[string]any{}
		for i, c := range cols {
			out[c] = vals[i]
		}
		_ = enc.Encode(out)
	}
}

// suppress unused-import in some builds.
var _ = os.Stdout
