package control

import (
	"crypto/subtle"
	"database/sql"
	"encoding/base64"
	"encoding/json"
	"net/http"
	"strconv"
	"strings"
	"time"

	"github.com/gin-gonic/gin"
)

// Alert identities are server-bound credentials, never a client-supplied actor.
type AlertOperator struct {
	Actor string `json:"actor"`
	Token string `json:"token"`
}

func (o *Operations) Register(r *gin.Engine, operators []AlertOperator) {
	g := r.Group("/api/v1")
	g.GET("/tasks", func(c *gin.Context) {
		items, err := o.TaskViews(c.Request.Context(), time.Now().UTC())
		if err != nil {
			writeAPIError(c, 503, "catalog_unavailable", "任务运行状态暂不可读", nil)
			return
		}
		c.Header("Cache-Control", "no-store")
		c.JSON(200, gin.H{"items": items, "environment": o.Environment})
	})
	g.GET("/dependencies", func(c *gin.Context) {
		runs, err := o.Runs(c.Request.Context(), RunFilter{Provider: "crm_load", Limit: 50})
		if err != nil {
			writeAPIError(c, 503, "lineage_unavailable", "流转证据暂不可读", nil)
			return
		}
		observed := []ProviderRun{}
		for _, run := range runs {
			if run.Manifest != nil {
				observed = append(observed, run)
			}
		}
		c.Header("Cache-Control", "no-store")
		c.JSON(200, gin.H{"items": OperationDependencies(), "kind": "design", "runtime_lineage": "current_manifest_evidence", "observed_runs": observed, "observed_limit": 50})
	})
	g.GET("/runs", func(c *gin.Context) {
		f := RunFilter{Task: c.Query("task"), Provider: c.Query("provider"), Status: c.Query("status")}
		if raw := c.Query("limit"); raw != "" {
			n, err := strconv.Atoi(raw)
			if err != nil || n < 1 || n > 100 {
				writeAPIError(c, 422, "invalid_limit", "limit must be 1..100", nil)
				return
			}
			f.Limit = n
		} else {
			f.Limit = 50
		}
		if cursor := c.Query("cursor"); cursor != "" {
			b, err := base64.RawURLEncoding.DecodeString(cursor)
			parts := strings.Split(string(b), "|")
			if err != nil || len(parts) != 2 {
				writeAPIError(c, 422, "invalid_cursor", "invalid cursor", nil)
				return
			}
			if _, err = time.Parse(time.RFC3339Nano, parts[0]); err != nil {
				writeAPIError(c, 422, "invalid_cursor", "invalid cursor time", nil)
				return
			}
			f.BeforeTime = parts[0]
			f.BeforeID = parts[1]
		}
		items, err := o.Runs(c.Request.Context(), f)
		if err != nil {
			writeAPIError(c, 503, "history_unavailable", "运行历史暂不可读", nil)
			return
		}
		cursor := ""
		if len(items) == f.Limit {
			last := items[len(items)-1]
			cursor = base64.RawURLEncoding.EncodeToString([]byte(stamp(last.StartedAt) + "|" + last.ID))
		}
		c.Header("Cache-Control", "no-store")
		c.JSON(200, gin.H{"items": items, "next_cursor": cursor, "environment": o.Environment})
	})
	g.GET("/runs/:id", func(c *gin.Context) {
		var p string
		err := o.db.QueryRowContext(c.Request.Context(), "SELECT payload FROM runs WHERE id=?", c.Param("id")).Scan(&p)
		if err == sql.ErrNoRows {
			writeAPIError(c, 404, "run_not_found", "运行不存在", nil)
			return
		}
		if err != nil {
			writeAPIError(c, 503, "history_unavailable", "运行不可读", nil)
			return
		}
		c.Data(200, "application/json", []byte(p))
	})
	for path, table := range map[string]string{"/schedules": "schedules", "/triggers": "triggers", "/collection-status": "checkpoints"} {
		path, table := path, table
		g.GET(path, func(c *gin.Context) {
			orderBy := "rowid DESC"
			if table == "triggers" {
				orderBy = "julianday(json_extract(payload,'$.at')) DESC, id DESC"
			}
			rows, err := o.db.QueryContext(c.Request.Context(), "SELECT payload FROM "+table+" ORDER BY "+orderBy+" LIMIT 500")
			if err != nil {
				writeAPIError(c, 503, "projection_unavailable", "观察投影不可读", nil)
				return
			}
			defer rows.Close()
			items := []json.RawMessage{}
			for rows.Next() {
				var p string
				if err = rows.Scan(&p); err != nil {
					break
				}
				if table == "checkpoints" {
					var state map[string]json.RawMessage
					if err = json.Unmarshal([]byte(p), &state); err != nil {
						break
					}
					delete(state, "cursor")
					var encoded []byte
					if encoded, err = json.Marshal(state); err != nil {
						break
					}
					p = string(encoded)
				}
				items = append(items, json.RawMessage(p))
			}
			if err != nil || rows.Err() != nil {
				writeAPIError(c, 503, "projection_unavailable", "观察投影不完整", nil)
				return
			}
			c.JSON(200, gin.H{"items": items, "limit": 500, "coverage": "bounded projection", "observed_at": time.Now().UTC()})
		})
	}
	g.GET("/alerts", func(c *gin.Context) {
		rows, err := o.db.QueryContext(c.Request.Context(), `SELECT a.id,a.subject,a.rule,a.status,a.opened,COALESCE(a.resolved,''),a.actor,a.message,d.status,d.attempts,d.message,COALESCE(r.provider,'') FROM alerts a LEFT JOIN deliveries d ON d.alert_id=a.id LEFT JOIN runs r ON r.id=a.subject ORDER BY a.opened DESC LIMIT 500`)
		if err != nil {
			writeAPIError(c, 503, "alerts_unavailable", "告警暂不可读", nil)
			return
		}
		defer rows.Close()
		items := []gin.H{}
		for rows.Next() {
			var id, subject, rule, status, opened, resolved, actor, message, delivery, deliveryMessage, provider string
			var attempts int
			if err = rows.Scan(&id, &subject, &rule, &status, &opened, &resolved, &actor, &message, &delivery, &attempts, &deliveryMessage, &provider); err != nil {
				break
			}
			impact := "not_verified"
			if provider == "crm_extract" || provider == "crm_load_job" {
				impact = "shared_job_unverified"
				message = "共享CRM进程失败（诊断）；财务子集影响未核实，不能直接判定财务表失败。" + message
			}
			items = append(items, gin.H{"id": id, "subject": subject, "rule": rule, "status": status, "opened_at": opened, "resolved_at": resolved, "actor": actor, "message": message, "impact_scope": impact, "delivery_status": delivery, "delivery_attempts": attempts, "delivery_message": deliveryMessage})
		}
		if err != nil || rows.Err() != nil {
			writeAPIError(c, 503, "alerts_unavailable", "告警读取不完整", nil)
			return
		}
		c.JSON(200, gin.H{"items": items, "limit": 500, "can_acknowledge": len(operators) > 0})
	})
	g.POST("/alerts/:id/acknowledge", func(c *gin.Context) {
		actor := ""
		token := strings.TrimPrefix(c.GetHeader("Authorization"), "Bearer ")
		for _, op := range operators {
			if op.Token != "" && subtle.ConstantTimeCompare([]byte(token), []byte(op.Token)) == 1 {
				actor = op.Actor
				break
			}
		}
		if actor == "" {
			writeAPIError(c, http.StatusForbidden, "alert_permission_required", "需要告警处理权限", nil)
			return
		}
		tx, err := o.db.BeginTx(c.Request.Context(), nil)
		if err != nil {
			writeAPIError(c, 503, "alert_store_unavailable", "告警存储不可用", nil)
			return
		}
		defer tx.Rollback()
		res, err := tx.Exec("UPDATE alerts SET status='acknowledged',actor=? WHERE id=? AND status='open'", actor, c.Param("id"))
		if err != nil {
			writeAPIError(c, 503, "alert_store_unavailable", "确认失败", nil)
			return
		}
		n, _ := res.RowsAffected()
		if n != 1 {
			writeAPIError(c, 409, "alert_state_conflict", "告警不存在或已确认/恢复", nil)
			return
		}
		_, err = tx.Exec("INSERT INTO alert_events(alert_id,at,action,actor) VALUES(?,?,'acknowledged',?)", c.Param("id"), stamp(time.Now()), actor)
		if err == nil {
			err = tx.Commit()
		}
		if err != nil {
			writeAPIError(c, 503, "alert_store_unavailable", "确认未持久化", nil)
			return
		}
		c.JSON(200, gin.H{"status": "acknowledged"})
	})
	// No dispatch without a provider's identity, lease, recovery and verifier contract.
	g.POST("/tasks/:key/actions", func(c *gin.Context) {
		for _, task := range OperationCatalog() {
			if task.Key == c.Param("key") {
				writeAPIError(c, 409, "execution_contract_unbound", task.Gap, gin.H{"task": task.Key, "scope": task.Scope})
				return
			}
		}
		writeAPIError(c, 404, "task_not_found", "任务不存在", nil)
	})
}
