package web

import (
	"net/http"
	"strconv"

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

// Handler handles web page requests
type Handler struct {
	db        *database.DB
	templates *Templates
	store     *storage.Storage
	review    *review.Service
	telemetry *telemetry.Service
}

// NewHandler creates a new web handler
func NewHandler(db *database.DB) (*Handler, error) {
	templates, err := NewTemplates()
	if err != nil {
		return nil, err
	}
	return &Handler{
		db:        db,
		templates: templates,
		review:    review.New(db),
	}, nil
}

// SetStorage sets the storage instance for the handler
func (h *Handler) SetStorage(store *storage.Storage) {
	h.store = store
}

// SetTelemetry sets the telemetry service for the handler
func (h *Handler) SetTelemetry(ts *telemetry.Service) {
	h.telemetry = ts
}

// PageData is the base data structure for all pages
type PageData struct {
	Title     string
	ActiveNav string
	Developer *database.Developer
	Error     string
}

// Login renders the login page
func (h *Handler) Login(w http.ResponseWriter, r *http.Request) {
	data := PageData{
		Title: "Sign In",
	}

	// Check for error message
	if err := r.URL.Query().Get("error"); err != "" {
		data.Error = err
	}

	h.render(w, "login", data)
}

// Dashboard renders the dashboard page
func (h *Handler) Dashboard(w http.ResponseWriter, r *http.Request) {
	developer := getDeveloperFromContext(r)
	if developer == nil {
		http.Redirect(w, r, "/login", http.StatusSeeOther)
		return
	}

	// Get stats
	apps, total, _ := h.db.ListApps(r.Context(), developer.ID, "", 1, 100)

	data := struct {
		PageData
		Stats struct {
			TotalApps   int
			Downloads   int64
			ActiveUsers int64
		}
		Apps []*database.App
	}{
		PageData: PageData{
			Title:     "Dashboard",
			ActiveNav: "dashboard",
			Developer: developer,
		},
		Apps: apps,
	}
	data.Stats.TotalApps = total
	data.Stats.Downloads = 0     // TODO: implement
	data.Stats.ActiveUsers = 0   // TODO: implement

	h.render(w, "dashboard", data)
}

// AppNew renders the new app form
func (h *Handler) AppNew(w http.ResponseWriter, r *http.Request) {
	developer := getDeveloperFromContext(r)
	if developer == nil {
		http.Redirect(w, r, "/login", http.StatusSeeOther)
		return
	}

	data := PageData{
		Title:     "Create New App",
		ActiveNav: "apps",
		Developer: developer,
	}

	h.render(w, "app_new", data)
}

// AppDetail renders the app detail page
func (h *Handler) AppDetail(w http.ResponseWriter, r *http.Request) {
	developer := getDeveloperFromContext(r)
	if developer == nil {
		http.Redirect(w, r, "/login", http.StatusSeeOther)
		return
	}

	appID := chi.URLParam(r, "appID")
	app, err := h.db.GetApp(r.Context(), appID)
	if err != nil {
		http.Error(w, "App not found", http.StatusNotFound)
		return
	}

	// Verify ownership
	if app.DeveloperID != developer.ID {
		http.Error(w, "Forbidden", http.StatusForbidden)
		return
	}

	// Get latest version
	versions, totalVersions, _ := h.db.ListVersions(r.Context(), appID, "", 1, 1)
	var latestVersion *database.AppVersion
	if len(versions) > 0 {
		latestVersion = versions[0]
	}

	data := struct {
		PageData
		App           *database.App
		Tab           string
		LatestVersion *database.AppVersion
		TotalVersions int
	}{
		PageData: PageData{
			Title:     app.Name,
			ActiveNav: "apps",
			Developer: developer,
		},
		App:           app,
		Tab:           "overview",
		LatestVersion: latestVersion,
		TotalVersions: totalVersions,
	}

	h.render(w, "app_detail", data)
}

// AppListPartial renders the app list partial for htmx
func (h *Handler) AppListPartial(w http.ResponseWriter, r *http.Request) {
	developer := getDeveloperFromContext(r)
	if developer == nil {
		http.Error(w, "Unauthorized", http.StatusUnauthorized)
		return
	}

	apps, _, err := h.db.ListApps(r.Context(), developer.ID, "", 1, 100)
	if err != nil {
		http.Error(w, "Failed to load apps", http.StatusInternalServerError)
		return
	}

	data := struct {
		Apps []*database.App
	}{
		Apps: apps,
	}

	h.renderPartial(w, "app_list", data)
}

// render renders a full page template
func (h *Handler) render(w http.ResponseWriter, name string, data interface{}) {
	w.Header().Set("Content-Type", "text/html; charset=utf-8")
	if err := h.templates.RenderPage(w, name, data); err != nil {
		http.Error(w, "Template error: "+err.Error(), http.StatusInternalServerError)
	}
}

// renderPartial renders a partial template
func (h *Handler) renderPartial(w http.ResponseWriter, name string, data interface{}) {
	w.Header().Set("Content-Type", "text/html; charset=utf-8")
	if err := h.templates.RenderPartial(w, name, data); err != nil {
		http.Error(w, "Template error: "+err.Error(), http.StatusInternalServerError)
	}
}

// getDeveloperFromContext retrieves the developer from the request context
// This will be set by the session middleware
func getDeveloperFromContext(r *http.Request) *database.Developer {
	developer, _ := r.Context().Value("developer").(*database.Developer)
	return developer
}

// AdminReviewQueue renders the admin review queue page
func (h *Handler) AdminReviewQueue(w http.ResponseWriter, r *http.Request) {
	developer := getDeveloperFromContext(r)
	if developer == nil {
		http.Redirect(w, r, "/login", http.StatusSeeOther)
		return
	}

	// TODO: Add admin role check here
	pending, approved, rejected, _ := h.db.GetReviewStats(r.Context())

	data := struct {
		PageData
		Stats struct {
			Pending  int
			Approved int
			Rejected int
		}
	}{
		PageData: PageData{
			Title:     "Review Queue",
			ActiveNav: "admin",
			Developer: developer,
		},
	}
	data.Stats.Pending = pending
	data.Stats.Approved = approved
	data.Stats.Rejected = rejected

	h.render(w, "admin_review_queue", data)
}

// AdminReviewDetail renders the review detail page
func (h *Handler) AdminReviewDetail(w http.ResponseWriter, r *http.Request) {
	developer := getDeveloperFromContext(r)
	if developer == nil {
		http.Redirect(w, r, "/login", http.StatusSeeOther)
		return
	}

	versionID := chi.URLParam(r, "versionID")
	vwa, err := h.db.GetVersionWithApp(r.Context(), versionID)
	if err != nil {
		http.Error(w, "Version not found", http.StatusNotFound)
		return
	}

	// Run validation if package exists
	var validationResult *review.FullValidationResult
	if h.store != nil && vwa.Version.PackageURL != "" {
		packagePath := h.store.GetPackagePath(vwa.Version.PackageURL)
		result, err := h.review.ValidatePackage(packagePath)
		if err == nil {
			validationResult = result
		}
	}

	data := struct {
		PageData
		App            *database.App
		Version        *database.AppVersion
		DeveloperName  string
		DeveloperEmail string
		Validation     *review.FullValidationResult
	}{
		PageData: PageData{
			Title:     "Review: " + vwa.App.Name,
			ActiveNav: "admin",
			Developer: developer,
		},
		App:            vwa.App,
		Version:        vwa.Version,
		DeveloperName:  vwa.DeveloperName,
		DeveloperEmail: vwa.DeveloperEmail,
		Validation:     validationResult,
	}

	h.render(w, "admin_review_detail", data)
}

// AdminReviewQueuePartial renders the review queue list partial for htmx
func (h *Handler) AdminReviewQueuePartial(w http.ResponseWriter, r *http.Request) {
	developer := getDeveloperFromContext(r)
	if developer == nil {
		http.Error(w, "Unauthorized", http.StatusUnauthorized)
		return
	}

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

	items, total, err := h.db.GetVersionsInReview(r.Context(), limit, offset)
	if err != nil {
		http.Error(w, "Failed to load queue", http.StatusInternalServerError)
		return
	}

	data := struct {
		Items      []database.VersionWithApp
		Pagination struct {
			Page       int
			Limit      int
			Total      int
			TotalPages int
		}
	}{
		Items: items,
	}
	data.Pagination.Page = page
	data.Pagination.Limit = limit
	data.Pagination.Total = total
	data.Pagination.TotalPages = (total + limit - 1) / limit

	h.renderPartial(w, "review_queue_list", data)
}

// AdminApprove handles approval of a version from the admin UI
func (h *Handler) AdminApprove(w http.ResponseWriter, r *http.Request) {
	developer := getDeveloperFromContext(r)
	if developer == nil {
		http.Error(w, "Unauthorized", http.StatusUnauthorized)
		return
	}

	versionID := chi.URLParam(r, "versionID")
	notes := r.FormValue("notes")

	// Verify version exists and is in review
	version, err := h.db.GetVersion(r.Context(), versionID)
	if err != nil {
		http.Error(w, "Version not found", http.StatusNotFound)
		return
	}

	if version.Status != "in_review" {
		http.Error(w, "Version is not in review", http.StatusBadRequest)
		return
	}

	// Approve the version
	if err := h.review.ApproveVersion(r.Context(), versionID, notes); err != nil {
		http.Error(w, "Failed to approve version: "+err.Error(), http.StatusInternalServerError)
		return
	}

	// Redirect back to queue via htmx
	w.Header().Set("HX-Redirect", "/admin/review-queue")
	w.WriteHeader(http.StatusOK)
}

// AdminReject handles rejection of a version from the admin UI
func (h *Handler) AdminReject(w http.ResponseWriter, r *http.Request) {
	developer := getDeveloperFromContext(r)
	if developer == nil {
		http.Error(w, "Unauthorized", http.StatusUnauthorized)
		return
	}

	versionID := chi.URLParam(r, "versionID")
	reason := r.FormValue("reason")
	message := r.FormValue("message")

	if reason == "" {
		http.Error(w, "Rejection reason is required", http.StatusBadRequest)
		return
	}

	// Verify version exists and is in review
	version, err := h.db.GetVersion(r.Context(), versionID)
	if err != nil {
		http.Error(w, "Version not found", http.StatusNotFound)
		return
	}

	if version.Status != "in_review" {
		http.Error(w, "Version is not in review", http.StatusBadRequest)
		return
	}

	// Reject the version
	feedback := &review.RejectionFeedback{
		Reason:      reason,
		Message:     message,
		CanResubmit: true,
	}
	if err := h.review.RejectVersion(r.Context(), versionID, feedback); err != nil {
		http.Error(w, "Failed to reject version: "+err.Error(), http.StatusInternalServerError)
		return
	}

	// Redirect back to queue via htmx
	w.Header().Set("HX-Redirect", "/admin/review-queue")
	w.WriteHeader(http.StatusOK)
}

// AdminValidate runs validation and returns HTML partial with results
func (h *Handler) AdminValidate(w http.ResponseWriter, r *http.Request) {
	developer := getDeveloperFromContext(r)
	if developer == nil {
		http.Error(w, "Unauthorized", http.StatusUnauthorized)
		return
	}

	versionID := chi.URLParam(r, "versionID")

	// Get version with package info
	version, err := h.db.GetVersion(r.Context(), versionID)
	if err != nil {
		http.Error(w, "Version not found", http.StatusNotFound)
		return
	}

	if version.PackageURL == "" {
		http.Error(w, "Version has no uploaded package", http.StatusBadRequest)
		return
	}

	// Run validation
	packagePath := h.store.GetPackagePath(version.PackageURL)
	result, err := h.review.ValidatePackage(packagePath)
	if err != nil {
		http.Error(w, "Validation error: "+err.Error(), http.StatusInternalServerError)
		return
	}

	// Render validation results partial
	h.renderPartial(w, "validation_results", result)
}

// AppAnalytics renders the app analytics page
func (h *Handler) AppAnalytics(w http.ResponseWriter, r *http.Request) {
	developer := getDeveloperFromContext(r)
	if developer == nil {
		http.Redirect(w, r, "/login", http.StatusSeeOther)
		return
	}

	appID := chi.URLParam(r, "appID")
	app, err := h.db.GetApp(r.Context(), appID)
	if err != nil {
		http.Error(w, "App not found", http.StatusNotFound)
		return
	}

	// Verify ownership
	if app.DeveloperID != developer.ID {
		http.Error(w, "Forbidden", http.StatusForbidden)
		return
	}

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

	// Get analytics data
	var overview *telemetry.AnalyticsOverview
	var eventStats []telemetry.DailyStats
	var crashes []*telemetry.CrashGroup

	if h.telemetry != nil {
		overview, _ = h.telemetry.GetAnalyticsOverview(r.Context(), appID, days)
		eventStats, _ = h.telemetry.GetDailyStats(r.Context(), appID, "", days)
		crashes, _, _ = h.telemetry.GetCrashGroups(r.Context(), appID, "open", 5, 0)
	}

	if overview == nil {
		overview = &telemetry.AnalyticsOverview{}
	}

	data := struct {
		PageData
		App        *database.App
		Days       int
		Overview   *telemetry.AnalyticsOverview
		EventStats []telemetry.DailyStats
		Crashes    []*telemetry.CrashGroup
	}{
		PageData: PageData{
			Title:     app.Name + " - Analytics",
			ActiveNav: "apps",
			Developer: developer,
		},
		App:        app,
		Days:       days,
		Overview:   overview,
		EventStats: eventStats,
		Crashes:    crashes,
	}

	h.render(w, "app_analytics", data)
}

// AppCrashes renders the app crashes page
func (h *Handler) AppCrashes(w http.ResponseWriter, r *http.Request) {
	developer := getDeveloperFromContext(r)
	if developer == nil {
		http.Redirect(w, r, "/login", http.StatusSeeOther)
		return
	}

	appID := chi.URLParam(r, "appID")
	app, err := h.db.GetApp(r.Context(), appID)
	if err != nil {
		http.Error(w, "App not found", http.StatusNotFound)
		return
	}

	// Verify ownership
	if app.DeveloperID != developer.ID {
		http.Error(w, "Forbidden", http.StatusForbidden)
		return
	}

	// Get status filter
	status := r.URL.Query().Get("status")
	if status == "" {
		status = "open"
	}

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

	// Get crashes
	var crashes []*telemetry.CrashGroup
	var total int
	if h.telemetry != nil {
		crashes, total, _ = h.telemetry.GetCrashGroups(r.Context(), appID, status, limit, offset)
	}

	data := struct {
		PageData
		App        *database.App
		Status     string
		Crashes    []*telemetry.CrashGroup
		Pagination struct {
			Page       int
			Limit      int
			Total      int
			TotalPages int
		}
	}{
		PageData: PageData{
			Title:     app.Name + " - Crashes",
			ActiveNav: "apps",
			Developer: developer,
		},
		App:     app,
		Status:  status,
		Crashes: crashes,
	}
	data.Pagination.Page = page
	data.Pagination.Limit = limit
	data.Pagination.Total = total
	data.Pagination.TotalPages = (total + limit - 1) / limit

	h.render(w, "app_crashes", data)
}
