package server

import (
	"database/sql"
	"encoding/json"
	"net/http"
	"strconv"
	"time"

	"github.com/operator/command-center/internal/notifications"
)

// NotificationDeps bundles Phase 6 dependencies.
type NotificationDeps struct {
	Dispatcher *notifications.Dispatcher
	VAPID      *notifications.VAPIDStore
}

type vapidResponse struct {
	PublicKey string `json:"public_key"`
}

func (s *Server) handlePushPublicKey(w http.ResponseWriter, r *http.Request) {
	if s.notifications == nil || s.notifications.VAPID == nil {
		http.Error(w, "push not configured", http.StatusServiceUnavailable)
		return
	}
	pub, err := s.notifications.VAPID.PublicKey(r.Context())
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	writeJSON(w, http.StatusOK, vapidResponse{PublicKey: pub})
}

type subscribeRequest struct {
	Endpoint    string `json:"endpoint"`
	Keys        struct {
		P256dh string `json:"p256dh"`
		Auth   string `json:"auth"`
	} `json:"keys"`
	DeviceLabel string `json:"device_label,omitempty"`
}

func (s *Server) handlePushSubscribe(w http.ResponseWriter, r *http.Request) {
	if s.sqlite == nil {
		http.Error(w, "sqlite required", http.StatusServiceUnavailable)
		return
	}
	var req subscribeRequest
	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
		http.Error(w, "bad json: "+err.Error(), http.StatusBadRequest)
		return
	}
	if req.Endpoint == "" || req.Keys.P256dh == "" || req.Keys.Auth == "" {
		http.Error(w, "endpoint, keys.p256dh, keys.auth required", http.StatusBadRequest)
		return
	}
	res, err := s.sqlite.ExecContext(r.Context(), `
		INSERT INTO push_subscriptions(endpoint, p256dh, auth, user_agent, device_label, created_at)
		VALUES (?, ?, ?, ?, ?, ?)
		ON CONFLICT(endpoint) DO UPDATE SET
		  p256dh = excluded.p256dh,
		  auth = excluded.auth,
		  user_agent = excluded.user_agent,
		  device_label = excluded.device_label,
		  failure_count = 0
	`, req.Endpoint, req.Keys.P256dh, req.Keys.Auth,
		nullableText(r.Header.Get("User-Agent")), nullableText(req.DeviceLabel),
		time.Now().Unix())
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	id, _ := res.LastInsertId()
	writeJSON(w, http.StatusCreated, map[string]any{"id": id, "ok": true})
}

func (s *Server) handlePushUnsubscribe(w http.ResponseWriter, r *http.Request) {
	if s.sqlite == nil {
		http.Error(w, "sqlite required", http.StatusServiceUnavailable)
		return
	}
	id := r.URL.Path
	// Last path segment.
	for i := len(id) - 1; i >= 0; i-- {
		if id[i] == '/' {
			id = id[i+1:]
			break
		}
	}
	_, err := s.sqlite.ExecContext(r.Context(), `DELETE FROM push_subscriptions WHERE id = ?`, id)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	writeJSON(w, http.StatusOK, map[string]any{"ok": true})
}

func (s *Server) handlePushSubscriptions(w http.ResponseWriter, r *http.Request) {
	if s.sqlite == nil {
		http.Error(w, "sqlite required", http.StatusServiceUnavailable)
		return
	}
	rows, err := s.sqlite.QueryContext(r.Context(), `
		SELECT id, endpoint, user_agent, device_label, created_at,
		       last_delivery_at, failure_count
		FROM push_subscriptions
		ORDER BY created_at DESC
	`)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	defer rows.Close()
	type sub struct {
		ID             int64  `json:"id"`
		Endpoint       string `json:"endpoint"`
		UserAgent      string `json:"user_agent,omitempty"`
		DeviceLabel    string `json:"device_label,omitempty"`
		CreatedAt      int64  `json:"created_at"`
		LastDeliveryAt *int64 `json:"last_delivery_at,omitempty"`
		FailureCount   int    `json:"failure_count"`
	}
	out := []sub{}
	for rows.Next() {
		var (
			s        sub
			ua, dl   sql.NullString
			last     sql.NullInt64
		)
		if err := rows.Scan(&s.ID, &s.Endpoint, &ua, &dl, &s.CreatedAt, &last, &s.FailureCount); err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}
		if ua.Valid {
			s.UserAgent = ua.String
		}
		if dl.Valid {
			s.DeviceLabel = dl.String
		}
		if last.Valid {
			v := last.Int64
			s.LastDeliveryAt = &v
		}
		out = append(out, s)
	}
	writeJSON(w, http.StatusOK, map[string]any{"count": len(out), "subscriptions": out})
}

type ruleDTO struct {
	ID              int64  `json:"id"`
	Name            string `json:"name"`
	Enabled         bool   `json:"enabled"`
	TriggerType     string `json:"trigger_type"`
	Channels        string `json:"channels_json"`
	CooldownSeconds int    `json:"cooldown_seconds"`
	LastFiredAt     *int64 `json:"last_fired_at,omitempty"`
	Source          string `json:"source"`
}

func (s *Server) handleListRules(w http.ResponseWriter, r *http.Request) {
	if s.sqlite == nil {
		http.Error(w, "sqlite required", http.StatusServiceUnavailable)
		return
	}
	rows, err := s.sqlite.QueryContext(r.Context(), `
		SELECT id, name, enabled, trigger_type, channels_json, cooldown_seconds,
		       last_fired_at, source
		FROM notification_rules ORDER BY name
	`)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	defer rows.Close()
	out := []ruleDTO{}
	for rows.Next() {
		var (
			d       ruleDTO
			enabled int
			last    sql.NullInt64
		)
		if err := rows.Scan(&d.ID, &d.Name, &enabled, &d.TriggerType, &d.Channels,
			&d.CooldownSeconds, &last, &d.Source); err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}
		d.Enabled = enabled != 0
		if last.Valid {
			v := last.Int64
			d.LastFiredAt = &v
		}
		out = append(out, d)
	}
	writeJSON(w, http.StatusOK, map[string]any{"count": len(out), "rules": out})
}

type testNotificationReq struct {
	Channels []string                  `json:"channels"`
	Title    string                    `json:"title"`
	Body     string                    `json:"body"`
	Config   map[string]map[string]any `json:"channel_config,omitempty"`
}

func (s *Server) handleTestNotification(w http.ResponseWriter, r *http.Request) {
	if s.notifications == nil || s.notifications.Dispatcher == nil {
		http.Error(w, "notifications not configured", http.StatusServiceUnavailable)
		return
	}
	var req testNotificationReq
	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}
	if req.Title == "" {
		req.Title = "Test from Command Center"
	}
	if req.Body == "" {
		req.Body = "If you can see this, push delivery works."
	}
	if len(req.Channels) == 0 {
		req.Channels = []string{"push"}
	}
	s.notifications.Dispatcher.Dispatch(r.Context(), notifications.Notification{
		Title:    req.Title,
		Body:     req.Body,
		Severity: notifications.SeverityInfo,
		URL:      "/",
	}, req.Channels, req.Config)
	writeJSON(w, http.StatusAccepted, map[string]any{"ok": true})
}

func (s *Server) handleNotificationLog(w http.ResponseWriter, r *http.Request) {
	if s.notifications == nil || s.notifications.Dispatcher == nil {
		http.Error(w, "notifications not configured", http.StatusServiceUnavailable)
		return
	}
	limit := 100
	if v := r.URL.Query().Get("limit"); v != "" {
		if n, _ := strconv.Atoi(v); n > 0 {
			limit = n
		}
	}
	rows, err := s.notifications.Dispatcher.LoadRecent(r.Context(), limit)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	writeJSON(w, http.StatusOK, map[string]any{"count": len(rows), "entries": rows})
}

func nullableText(s string) any {
	if s == "" {
		return nil
	}
	return s
}
