package handlers

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

	"github.com/go-chi/chi/v5"
	"github.com/google/uuid"
	"omixlab.com/mosis-portal/internal/api/middleware"
	"omixlab.com/mosis-portal/internal/database"
	"omixlab.com/mosis-portal/internal/storage"
)

// AppHandler handles app-related endpoints
type AppHandler struct {
	db      *database.DB
	storage *storage.Storage
}

// NewAppHandler creates a new app handler
func NewAppHandler(db *database.DB, store *storage.Storage) *AppHandler {
	return &AppHandler{db: db, storage: store}
}

// CreateAppRequest is the request body for creating an app
type CreateAppRequest struct {
	PackageID   string `json:"package_id" validate:"required,package_id"`
	Name        string `json:"name" validate:"required,min=1,max=50"`
	Description string `json:"description,omitempty" validate:"max=500"`
	Category    string `json:"category,omitempty" validate:"max=50"`
}

// UpdateAppRequest is the request body for updating an app
type UpdateAppRequest struct {
	Name        *string  `json:"name,omitempty" validate:"omitempty,min=1,max=50"`
	Description *string  `json:"description,omitempty" validate:"omitempty,max=500"`
	Category    *string  `json:"category,omitempty" validate:"omitempty,max=50"`
	Tags        []string `json:"tags,omitempty" validate:"omitempty,max=10,dive,max=30"`
}

// List lists the developer's apps
func (h *AppHandler) List(w http.ResponseWriter, r *http.Request) {
	developerID := middleware.GetDeveloperID(r.Context())
	if developerID == "" {
		Error(w, "unauthorized", http.StatusUnauthorized)
		return
	}

	// Parse query parameters
	status := r.URL.Query().Get("status")
	page, _ := strconv.Atoi(r.URL.Query().Get("page"))
	limit, _ := strconv.Atoi(r.URL.Query().Get("limit"))

	if page < 1 {
		page = 1
	}
	if limit < 1 || limit > 100 {
		limit = 20
	}

	apps, total, err := h.db.ListApps(r.Context(), developerID, status, page, limit)
	if err != nil {
		Error(w, "failed to list apps: "+err.Error(), http.StatusInternalServerError)
		return
	}

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

// Create creates a new app
func (h *AppHandler) Create(w http.ResponseWriter, r *http.Request) {
	developerID := middleware.GetDeveloperID(r.Context())
	if developerID == "" {
		Error(w, "unauthorized", http.StatusUnauthorized)
		return
	}

	var req CreateAppRequest
	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
		Error(w, "invalid request body", http.StatusBadRequest)
		return
	}

	// Validate package ID format
	if !isValidPackageID(req.PackageID) {
		Error(w, "invalid package_id format (must be like com.example.app)", http.StatusBadRequest)
		return
	}

	// Check if package_id already exists
	existing, _ := h.db.GetAppByPackageID(r.Context(), req.PackageID)
	if existing != nil {
		Error(w, "package_id already exists", http.StatusConflict)
		return
	}

	app, err := h.db.CreateApp(r.Context(), &database.App{
		DeveloperID: developerID,
		PackageID:   req.PackageID,
		Name:        req.Name,
		Description: req.Description,
		Category:    req.Category,
		Status:      "draft",
	})
	if err != nil {
		Error(w, "failed to create app: "+err.Error(), http.StatusInternalServerError)
		return
	}

	JSON(w, http.StatusCreated, app)
}

// Get retrieves a specific app
func (h *AppHandler) Get(w http.ResponseWriter, r *http.Request) {
	developerID := middleware.GetDeveloperID(r.Context())
	if developerID == "" {
		Error(w, "unauthorized", http.StatusUnauthorized)
		return
	}

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

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

	JSON(w, http.StatusOK, app)
}

// Update updates an app
func (h *AppHandler) Update(w http.ResponseWriter, r *http.Request) {
	developerID := middleware.GetDeveloperID(r.Context())
	if developerID == "" {
		Error(w, "unauthorized", http.StatusUnauthorized)
		return
	}

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

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

	var req UpdateAppRequest
	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
		Error(w, "invalid request body", http.StatusBadRequest)
		return
	}

	// Apply updates
	if req.Name != nil {
		app.Name = *req.Name
	}
	if req.Description != nil {
		app.Description = *req.Description
	}
	if req.Category != nil {
		app.Category = *req.Category
	}
	if req.Tags != nil {
		app.Tags = req.Tags
	}

	if err := h.db.UpdateApp(r.Context(), app); err != nil {
		Error(w, "failed to update app: "+err.Error(), http.StatusInternalServerError)
		return
	}

	JSON(w, http.StatusOK, app)
}

// Delete deletes an app
func (h *AppHandler) Delete(w http.ResponseWriter, r *http.Request) {
	developerID := middleware.GetDeveloperID(r.Context())
	if developerID == "" {
		Error(w, "unauthorized", http.StatusUnauthorized)
		return
	}

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

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

	// Check if app has published versions
	hasPublished, err := h.db.AppHasPublishedVersions(r.Context(), appID)
	if err != nil {
		Error(w, "failed to check versions: "+err.Error(), http.StatusInternalServerError)
		return
	}

	if hasPublished {
		Error(w, "cannot delete app with published versions", http.StatusBadRequest)
		return
	}

	if err := h.db.DeleteApp(r.Context(), appID); err != nil {
		Error(w, "failed to delete app: "+err.Error(), http.StatusInternalServerError)
		return
	}

	JSON(w, http.StatusOK, map[string]bool{"success": true})
}

// ListVersions lists versions for an app
func (h *AppHandler) ListVersions(w http.ResponseWriter, r *http.Request) {
	developerID := middleware.GetDeveloperID(r.Context())
	if developerID == "" {
		Error(w, "unauthorized", http.StatusUnauthorized)
		return
	}

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

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

	// Parse query parameters
	status := r.URL.Query().Get("status")
	page, _ := strconv.Atoi(r.URL.Query().Get("page"))
	limit, _ := strconv.Atoi(r.URL.Query().Get("limit"))

	if page < 1 {
		page = 1
	}
	if limit < 1 || limit > 100 {
		limit = 20
	}

	versions, total, err := h.db.ListVersions(r.Context(), appID, status, page, limit)
	if err != nil {
		Error(w, "failed to list versions: "+err.Error(), http.StatusInternalServerError)
		return
	}

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

// CreateVersionRequest is the request body for creating a version
type CreateVersionRequest struct {
	VersionName  string `json:"version_name" validate:"required"`
	VersionCode  int    `json:"version_code" validate:"required,min=1"`
	ReleaseNotes string `json:"release_notes,omitempty" validate:"max=5000"`
}

// CreateVersion creates a new app version
func (h *AppHandler) CreateVersion(w http.ResponseWriter, r *http.Request) {
	developerID := middleware.GetDeveloperID(r.Context())
	if developerID == "" {
		Error(w, "unauthorized", http.StatusUnauthorized)
		return
	}

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

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

	var req CreateVersionRequest
	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
		Error(w, "invalid request body", http.StatusBadRequest)
		return
	}

	// Check if version code already exists
	exists, err := h.db.VersionCodeExists(r.Context(), appID, req.VersionCode)
	if err != nil {
		Error(w, "failed to check version: "+err.Error(), http.StatusInternalServerError)
		return
	}
	if exists {
		Error(w, "version_code already exists for this app", http.StatusConflict)
		return
	}

	version, err := h.db.CreateVersion(r.Context(), &database.AppVersion{
		AppID:        appID,
		VersionName:  req.VersionName,
		VersionCode:  req.VersionCode,
		ReleaseNotes: req.ReleaseNotes,
		Status:       "draft",
	})
	if err != nil {
		Error(w, "failed to create version: "+err.Error(), http.StatusInternalServerError)
		return
	}

	// Generate upload URL (for now, just a placeholder path)
	uploadURL := "/packages/" + appID + "/" + version.ID + ".mosis"
	uploadExpires := time.Now().Add(1 * time.Hour).Format(time.RFC3339)

	JSON(w, http.StatusCreated, map[string]interface{}{
		"version":        version,
		"upload_url":     uploadURL,
		"upload_expires": uploadExpires,
	})
}

// GetVersion retrieves a specific version
func (h *AppHandler) GetVersion(w http.ResponseWriter, r *http.Request) {
	developerID := middleware.GetDeveloperID(r.Context())
	if developerID == "" {
		Error(w, "unauthorized", http.StatusUnauthorized)
		return
	}

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

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

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

	version, err := h.db.GetVersion(r.Context(), versionID)
	if err != nil || version.AppID != appID {
		Error(w, "version not found", http.StatusNotFound)
		return
	}

	JSON(w, http.StatusOK, version)
}

// SubmitVersion submits a version for review
func (h *AppHandler) SubmitVersion(w http.ResponseWriter, r *http.Request) {
	developerID := middleware.GetDeveloperID(r.Context())
	if developerID == "" {
		Error(w, "unauthorized", http.StatusUnauthorized)
		return
	}

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

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

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

	version, err := h.db.GetVersion(r.Context(), versionID)
	if err != nil || version.AppID != appID {
		Error(w, "version not found", http.StatusNotFound)
		return
	}

	// Check current status
	if version.Status != "draft" && version.Status != "rejected" {
		Error(w, "version cannot be submitted (current status: "+version.Status+")", http.StatusBadRequest)
		return
	}

	// Check if package is uploaded
	if version.PackageURL == "" {
		Error(w, "package must be uploaded before submitting", http.StatusBadRequest)
		return
	}

	version.Status = "review"
	if err := h.db.UpdateVersionStatus(r.Context(), versionID, "review"); err != nil {
		Error(w, "failed to submit version: "+err.Error(), http.StatusInternalServerError)
		return
	}

	JSON(w, http.StatusOK, version)
}

// PublishVersion publishes an approved version
func (h *AppHandler) PublishVersion(w http.ResponseWriter, r *http.Request) {
	developerID := middleware.GetDeveloperID(r.Context())
	if developerID == "" {
		Error(w, "unauthorized", http.StatusUnauthorized)
		return
	}

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

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

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

	version, err := h.db.GetVersion(r.Context(), versionID)
	if err != nil || version.AppID != appID {
		Error(w, "version not found", http.StatusNotFound)
		return
	}

	// Check current status - must be approved
	if version.Status != "approved" {
		Error(w, "version must be approved before publishing (current status: "+version.Status+")", http.StatusBadRequest)
		return
	}

	now := time.Now()
	version.Status = "published"
	version.PublishedAt = &now

	if err := h.db.PublishVersion(r.Context(), versionID); err != nil {
		Error(w, "failed to publish version: "+err.Error(), http.StatusInternalServerError)
		return
	}

	// Update app status to published
	if app.Status == "draft" {
		app.Status = "published"
		h.db.UpdateAppStatus(r.Context(), appID, "published")
	}

	JSON(w, http.StatusOK, version)
}

// isValidPackageID validates package ID format (com.example.app)
func isValidPackageID(id string) bool {
	if len(id) < 5 || len(id) > 100 {
		return false
	}
	// Simple validation: lowercase letters, digits, and dots; must have at least 2 dots
	dots := 0
	for i, c := range id {
		if c == '.' {
			dots++
			// No consecutive dots, no leading/trailing dots
			if i == 0 || i == len(id)-1 {
				return false
			}
			continue
		}
		if (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || c == '_' {
			continue
		}
		return false
	}
	return dots >= 2
}

// UploadPackage handles package file upload via multipart form
func (h *AppHandler) UploadPackage(w http.ResponseWriter, r *http.Request) {
	developerID := middleware.GetDeveloperID(r.Context())
	if developerID == "" {
		Error(w, "unauthorized", http.StatusUnauthorized)
		return
	}

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

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

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

	// Check version status - must be draft
	if version.Status != "draft" {
		Error(w, "package can only be uploaded to draft versions", http.StatusBadRequest)
		return
	}

	// Parse multipart form (max 50MB)
	if err := r.ParseMultipartForm(50 << 20); err != nil {
		Error(w, "failed to parse form: "+err.Error(), http.StatusBadRequest)
		return
	}

	file, header, err := r.FormFile("package")
	if err != nil {
		Error(w, "no package file provided", http.StatusBadRequest)
		return
	}
	defer file.Close()

	// Validate file extension
	if header.Filename[len(header.Filename)-6:] != ".mosis" {
		Error(w, "package file must have .mosis extension", http.StatusBadRequest)
		return
	}

	// Generate upload ID and save to temp
	uploadID := uuid.New().String()
	size, err := h.storage.SaveTempFile(uploadID, file)
	if err != nil {
		Error(w, "failed to save package: "+err.Error(), http.StatusInternalServerError)
		return
	}

	// TODO: Validate package (signature, manifest extraction)
	// For now, just move to final location

	// Move to final storage
	if err := h.storage.MoveTempToFinal(uploadID, developerID, appID, version.VersionCode); err != nil {
		h.storage.DeleteTemp(uploadID)
		Error(w, "failed to store package: "+err.Error(), http.StatusInternalServerError)
		return
	}

	// Update version record with package info
	packagePath := h.storage.PackagePath(developerID, appID, version.VersionCode)
	version.PackageURL = packagePath
	version.PackageSize = size

	if err := h.db.UpdateVersionPackage(r.Context(), versionID, packagePath, size); err != nil {
		Error(w, "failed to update version: "+err.Error(), http.StatusInternalServerError)
		return
	}

	JSON(w, http.StatusOK, map[string]interface{}{
		"success":      true,
		"package_size": size,
		"package_url":  fmt.Sprintf("/downloads/%s/%s/%d/package.mosis", developerID, appID, version.VersionCode),
	})
}

// UploadIcon handles app icon upload
func (h *AppHandler) UploadIcon(w http.ResponseWriter, r *http.Request) {
	developerID := middleware.GetDeveloperID(r.Context())
	if developerID == "" {
		Error(w, "unauthorized", http.StatusUnauthorized)
		return
	}

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

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

	// Parse multipart form (max 10MB)
	if err := r.ParseMultipartForm(10 << 20); err != nil {
		Error(w, "failed to parse form: "+err.Error(), http.StatusBadRequest)
		return
	}

	file, _, err := r.FormFile("icon")
	if err != nil {
		Error(w, "no icon file provided", http.StatusBadRequest)
		return
	}
	defer file.Close()

	// Process icon (validate, resize to multiple sizes)
	if err := h.storage.ProcessIcon(appID, file); err != nil {
		Error(w, "failed to process icon: "+err.Error(), http.StatusBadRequest)
		return
	}

	// Return icon URLs
	iconURLs := make(map[string]string)
	for _, size := range storage.IconSizes {
		iconURLs[fmt.Sprintf("%d", size)] = fmt.Sprintf("/assets/%s/icon-%d.png", appID, size)
	}

	JSON(w, http.StatusOK, map[string]interface{}{
		"success": true,
		"icons":   iconURLs,
	})
}
