package handlers

import (
	"encoding/json"
	"net/http"
	"strconv"

	"github.com/go-chi/chi/v5"
	"omixlab.com/mosis-portal/internal/database"
	"omixlab.com/mosis-portal/internal/telemetry"
)

// TelemetryHandler handles telemetry API requests
type TelemetryHandler struct {
	db        *database.DB
	telemetry *telemetry.Service
}

// NewTelemetryHandler creates a new telemetry handler
func NewTelemetryHandler(db *database.DB, ts *telemetry.Service) *TelemetryHandler {
	return &TelemetryHandler{
		db:        db,
		telemetry: ts,
	}
}

// RecordEvents handles POST /v1/telemetry/events
func (h *TelemetryHandler) RecordEvents(w http.ResponseWriter, r *http.Request) {
	var batch telemetry.EventBatch
	if err := json.NewDecoder(r.Body).Decode(&batch); err != nil {
		Error(w, http.StatusBadRequest, "invalid_body", "Invalid request body")
		return
	}

	if batch.AppID == "" {
		Error(w, http.StatusBadRequest, "missing_app_id", "App ID is required")
		return
	}

	if batch.DeviceID == "" {
		Error(w, http.StatusBadRequest, "missing_device_id", "Device ID is required")
		return
	}

	if len(batch.Events) == 0 {
		Error(w, http.StatusBadRequest, "no_events", "At least one event is required")
		return
	}

	count, err := h.telemetry.RecordEvents(r.Context(), &batch)
	if err != nil {
		Error(w, http.StatusInternalServerError, "record_error", err.Error())
		return
	}

	JSON(w, http.StatusOK, map[string]interface{}{
		"received": count,
	})
}

// RecordCrash handles POST /v1/telemetry/crash
func (h *TelemetryHandler) RecordCrash(w http.ResponseWriter, r *http.Request) {
	var report telemetry.CrashReport
	if err := json.NewDecoder(r.Body).Decode(&report); err != nil {
		Error(w, http.StatusBadRequest, "invalid_body", "Invalid request body")
		return
	}

	if report.AppID == "" {
		Error(w, http.StatusBadRequest, "missing_app_id", "App ID is required")
		return
	}

	if report.DeviceID == "" {
		Error(w, http.StatusBadRequest, "missing_device_id", "Device ID is required")
		return
	}

	if report.Crash.Type == "" {
		Error(w, http.StatusBadRequest, "missing_crash_type", "Crash type is required")
		return
	}

	groupID, err := h.telemetry.RecordCrash(r.Context(), &report)
	if err != nil {
		Error(w, http.StatusInternalServerError, "record_error", err.Error())
		return
	}

	JSON(w, http.StatusOK, map[string]interface{}{
		"id": groupID,
	})
}

// GetAnalyticsOverview handles GET /v1/apps/:appID/analytics/overview
func (h *TelemetryHandler) GetAnalyticsOverview(w http.ResponseWriter, r *http.Request) {
	appID := chi.URLParam(r, "appID")

	// Verify app ownership
	developerID := getDeveloperID(r)
	app, err := h.db.GetApp(r.Context(), appID)
	if err != nil {
		Error(w, http.StatusNotFound, "not_found", "App not found")
		return
	}
	if app.DeveloperID != developerID {
		Error(w, http.StatusForbidden, "forbidden", "Access denied")
		return
	}

	days := 30
	if d := r.URL.Query().Get("days"); d != "" {
		if parsed, err := strconv.Atoi(d); err == nil && parsed > 0 && parsed <= 365 {
			days = parsed
		}
	}

	overview, err := h.telemetry.GetAnalyticsOverview(r.Context(), appID, days)
	if err != nil {
		Error(w, http.StatusInternalServerError, "query_error", err.Error())
		return
	}

	JSON(w, http.StatusOK, overview)
}

// GetAnalyticsEvents handles GET /v1/apps/:appID/analytics/events
func (h *TelemetryHandler) GetAnalyticsEvents(w http.ResponseWriter, r *http.Request) {
	appID := chi.URLParam(r, "appID")

	// Verify app ownership
	developerID := getDeveloperID(r)
	app, err := h.db.GetApp(r.Context(), appID)
	if err != nil {
		Error(w, http.StatusNotFound, "not_found", "App not found")
		return
	}
	if app.DeveloperID != developerID {
		Error(w, http.StatusForbidden, "forbidden", "Access denied")
		return
	}

	eventType := r.URL.Query().Get("event_type")
	days := 30
	if d := r.URL.Query().Get("days"); d != "" {
		if parsed, err := strconv.Atoi(d); err == nil && parsed > 0 && parsed <= 365 {
			days = parsed
		}
	}

	stats, err := h.telemetry.GetDailyStats(r.Context(), appID, eventType, days)
	if err != nil {
		Error(w, http.StatusInternalServerError, "query_error", err.Error())
		return
	}

	JSON(w, http.StatusOK, map[string]interface{}{
		"data": stats,
	})
}

// GetCrashes handles GET /v1/apps/:appID/crashes
func (h *TelemetryHandler) GetCrashes(w http.ResponseWriter, r *http.Request) {
	appID := chi.URLParam(r, "appID")

	// Verify app ownership
	developerID := getDeveloperID(r)
	app, err := h.db.GetApp(r.Context(), appID)
	if err != nil {
		Error(w, http.StatusNotFound, "not_found", "App not found")
		return
	}
	if app.DeveloperID != developerID {
		Error(w, http.StatusForbidden, "forbidden", "Access denied")
		return
	}

	status := r.URL.Query().Get("status")
	page, _ := strconv.Atoi(r.URL.Query().Get("page"))
	if page < 1 {
		page = 1
	}
	limit, _ := strconv.Atoi(r.URL.Query().Get("limit"))
	if limit < 1 || limit > 100 {
		limit = 20
	}
	offset := (page - 1) * limit

	crashes, total, err := h.telemetry.GetCrashGroups(r.Context(), appID, status, limit, offset)
	if err != nil {
		Error(w, http.StatusInternalServerError, "query_error", err.Error())
		return
	}

	JSON(w, http.StatusOK, map[string]interface{}{
		"crashes": crashes,
		"pagination": map[string]int{
			"page":        page,
			"limit":       limit,
			"total":       total,
			"total_pages": (total + limit - 1) / limit,
		},
	})
}

// GetCrash handles GET /v1/apps/:appID/crashes/:crashID
func (h *TelemetryHandler) GetCrash(w http.ResponseWriter, r *http.Request) {
	appID := chi.URLParam(r, "appID")
	crashID := chi.URLParam(r, "crashID")

	// Verify app ownership
	developerID := getDeveloperID(r)
	app, err := h.db.GetApp(r.Context(), appID)
	if err != nil {
		Error(w, http.StatusNotFound, "not_found", "App not found")
		return
	}
	if app.DeveloperID != developerID {
		Error(w, http.StatusForbidden, "forbidden", "Access denied")
		return
	}

	crash, err := h.telemetry.GetCrashGroup(r.Context(), appID, crashID)
	if err != nil {
		Error(w, http.StatusNotFound, "not_found", "Crash group not found")
		return
	}

	JSON(w, http.StatusOK, crash)
}

// UpdateCrashStatus handles PATCH /v1/apps/:appID/crashes/:crashID
func (h *TelemetryHandler) UpdateCrashStatus(w http.ResponseWriter, r *http.Request) {
	appID := chi.URLParam(r, "appID")
	crashID := chi.URLParam(r, "crashID")

	// Verify app ownership
	developerID := getDeveloperID(r)
	app, err := h.db.GetApp(r.Context(), appID)
	if err != nil {
		Error(w, http.StatusNotFound, "not_found", "App not found")
		return
	}
	if app.DeveloperID != developerID {
		Error(w, http.StatusForbidden, "forbidden", "Access denied")
		return
	}

	var req struct {
		Status string `json:"status"`
	}
	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
		Error(w, http.StatusBadRequest, "invalid_body", "Invalid request body")
		return
	}

	if req.Status != "open" && req.Status != "resolved" && req.Status != "ignored" {
		Error(w, http.StatusBadRequest, "invalid_status", "Status must be open, resolved, or ignored")
		return
	}

	if err := h.telemetry.UpdateCrashGroupStatus(r.Context(), appID, crashID, req.Status); err != nil {
		Error(w, http.StatusInternalServerError, "update_error", err.Error())
		return
	}

	crash, _ := h.telemetry.GetCrashGroup(r.Context(), appID, crashID)
	JSON(w, http.StatusOK, crash)
}
