package api

import (
	"containr/internal/database"
	"context"
	"crypto/rand"
	"database/sql"
	"encoding/hex"
	"fmt"
	"net/http"
	"strings"
	"time"

	"github.com/gin-gonic/gin"
	"github.com/go-playground/validator/v10"
	"golang.org/x/crypto/bcrypt"
)

// Constants for validation and limits
const (
	MaxServiceNameLength = 100
	MaxRoutePrefixLength = 200
	MaxUpstreamURLLength = 500
	MaxAPIKeyNameLength  = 100
	MinAPIKeyLength      = 20
	MaxAPIKeyLength      = 100
	DefaultRPMLimit      = 60
	DefaultMonthlyQuota  = 1000
	MaxRPMLimit          = 10000
	MaxMonthlyQuota      = 10000000
)

// Validator instance for request validation
var validate = validator.New()

// APIError represents a structured API error response
type APIError struct {
	Code    string      `json:"code"`
	Message string      `json:"message"`
	Details interface{} `json:"details,omitempty"`
}

// APIResponse represents a standardized API response
type APIResponse struct {
	Success bool        `json:"success"`
	Data    interface{} `json:"data,omitempty"`
	Error   *APIError   `json:"error,omitempty"`
	Meta    *Meta       `json:"meta,omitempty"`
}

// Meta contains pagination and metadata
type Meta struct {
	Page       int `json:"page,omitempty"`
	PerPage    int `json:"per_page,omitempty"`
	Total      int `json:"total,omitempty"`
	TotalPages int `json:"total_pages,omitempty"`
}

// ServiceRequest represents the request payload for creating/updating services
type ServiceRequest struct {
	Name         string `json:"name" binding:"required,max=100" validate:"required,min=1,max=100"`
	UpstreamURL  string `json:"upstreamUrl" binding:"required,max=500" validate:"required,url,max=500"`
	RoutePrefix  string `json:"routePrefix" binding:"required,max=200" validate:"required,min=1,max=200"`
	Enabled      *bool  `json:"enabled,omitempty"`
	RPMLimit     *int   `json:"rpmLimit,omitempty" validate:"omitempty,min=1,max=10000"`
	MonthlyQuota *int   `json:"monthlyQuota,omitempty" validate:"omitempty,min=1,max=10000000"`
}

// APIKeyRequest represents the request payload for creating/updating API keys
type APIKeyRequest struct {
	Name         string `json:"name" binding:"required,max=100" validate:"required,min=1,max=100"`
	Plan         string `json:"plan" validate:"omitempty,oneof=free pro business enterprise"`
	Enabled      *bool  `json:"enabled,omitempty"`
	RPMLimit     *int   `json:"rpmLimit,omitempty" validate:"omitempty,min=1,max=10000"`
	MonthlyQuota *int   `json:"monthlyQuota,omitempty" validate:"omitempty,min=1,max=10000000"`
}

// generateServiceID generates a cryptographically secure service ID
func generateServiceID() (string, error) {
	bytes := make([]byte, 16)
	if _, err := rand.Read(bytes); err != nil {
		return "", fmt.Errorf("failed to generate random bytes: %w", err)
	}
	return "svc_" + hex.EncodeToString(bytes), nil
}

// generateAPIKey generates a cryptographically secure API key
func generateAPIKey() (string, error) {
	bytes := make([]byte, 32)
	if _, err := rand.Read(bytes); err != nil {
		return "", fmt.Errorf("failed to generate random bytes: %w", err)
	}
	return "ap_" + hex.EncodeToString(bytes), nil
}

// hashAPIKey creates a bcrypt hash for the API key
func hashAPIKey(key string) (string, error) {
	hash, err := bcrypt.GenerateFromPassword([]byte(key), bcrypt.DefaultCost)
	if err != nil {
		return "", fmt.Errorf("failed to hash API key: %w", err)
	}
	return string(hash), nil
}

// validateAPIKeyPlan validates and returns default values for API key plans
func validateAPIKeyPlan(plan string) (string, int, int, error) {
	if plan == "" {
		plan = "free"
	}

	switch plan {
	case "free":
		return plan, 60, 1000, nil
	case "pro":
		return plan, 600, 50000, nil
	case "business":
		return plan, 3000, 300000, nil
	case "enterprise":
		return plan, 10000, 10000000, nil
	default:
		return "", 0, 0, fmt.Errorf("invalid plan: %s", plan)
	}
}

// sendJSONResponse sends a standardized JSON response
func sendJSONResponse(c *gin.Context, statusCode int, response APIResponse) {
	c.JSON(statusCode, response)
}

// sendErrorResponse sends a standardized error response
func sendErrorResponse(c *gin.Context, statusCode int, code, message string, details interface{}) {
	response := APIResponse{
		Success: false,
		Error: &APIError{
			Code:    code,
			Message: message,
			Details: details,
		},
	}
	sendJSONResponse(c, statusCode, response)
}

// sendSuccessResponse sends a standardized success response
func sendSuccessResponse(c *gin.Context, statusCode int, data interface{}) {
	response := APIResponse{
		Success: true,
		Data:    data,
	}
	sendJSONResponse(c, statusCode, response)
}

// validateRequest validates the request payload using the validator
func validateRequest(c *gin.Context, req interface{}) error {
	if err := c.ShouldBindJSON(req); err != nil {
		return fmt.Errorf("invalid request body: %w", err)
	}

	if err := validate.Struct(req); err != nil {
		return fmt.Errorf("validation failed: %w", err)
	}

	return nil
}

// handleAPwhyServicesList returns a list of API services
func handleAPwhyServicesList(c *gin.Context) {
	db := c.MustGet("db").(*database.DB)
	ctx := context.Background()

	// Query services from database
	rows, err := db.QueryContext(ctx, `
		SELECT id, name, slug, upstream_url, route_prefix, enabled, created_at, updated_at
		FROM api_services 
		ORDER BY created_at DESC
	`)
	if err != nil {
		sendErrorResponse(c, http.StatusInternalServerError, "DATABASE_ERROR",
			"Failed to query services", err.Error())
		return
	}
	defer rows.Close()

	var services []map[string]interface{}
	for rows.Next() {
		var id, name, slug, upstreamURL, routePrefix, createdAt, updatedAt string
		var enabled bool
		err := rows.Scan(&id, &name, &slug, &upstreamURL, &routePrefix, &enabled, &createdAt, &updatedAt)
		if err != nil {
			continue // Skip malformed rows
		}

		services = append(services, map[string]interface{}{
			"id":          id,
			"name":        name,
			"slug":        slug,
			"upstreamUrl": upstreamURL,
			"routePrefix": routePrefix,
			"enabled":     enabled,
			"createdAt":   createdAt,
			"updatedAt":   updatedAt,
		})
	}

	sendSuccessResponse(c, http.StatusOK, map[string]interface{}{
		"services": services,
		"count":    len(services),
	})
}

// handleAPwhyServicesCreate creates a new API service
func handleAPwhyServicesCreate(c *gin.Context) {
	var req ServiceRequest

	if err := validateRequest(c, &req); err != nil {
		sendErrorResponse(c, http.StatusBadRequest, "VALIDATION_ERROR",
			"Invalid request parameters", err.Error())
		return
	}

	db := c.MustGet("db").(*database.DB)
	ctx := context.Background()

	// Generate slug and ID
	id, err := generateServiceID()
	if err != nil {
		sendErrorResponse(c, http.StatusInternalServerError, "GENERATION_ERROR",
			"Failed to generate service ID", err.Error())
		return
	}

	slug := strings.ToLower(strings.ReplaceAll(req.Name, " ", "-"))

	// Insert service into database
	query := `
		INSERT INTO api_services (
			id, name, slug, upstream_url, route_prefix, health_path,
			enabled, rpm_limit, monthly_quota, created_at, updated_at
		) VALUES ($1, $2, $3, $4, $5, '/health', true, $6, $7, NOW(), NOW())
	`

	var rpmLimit, monthlyQuota int
	if req.RPMLimit != nil {
		rpmLimit = *req.RPMLimit
	} else {
		rpmLimit = DefaultRPMLimit
	}

	if req.MonthlyQuota != nil {
		monthlyQuota = *req.MonthlyQuota
	} else {
		monthlyQuota = DefaultMonthlyQuota
	}

	_, err = db.ExecContext(ctx, query, id, req.Name, slug, req.UpstreamURL,
		req.RoutePrefix, rpmLimit, monthlyQuota)

	if err != nil {
		sendErrorResponse(c, http.StatusInternalServerError, "DATABASE_ERROR",
			"Failed to create service", err.Error())
		return
	}

	serviceData := map[string]interface{}{
		"id":           id,
		"name":         req.Name,
		"slug":         slug,
		"upstreamUrl":  req.UpstreamURL,
		"routePrefix":  req.RoutePrefix,
		"enabled":      true,
		"rpmLimit":     rpmLimit,
		"monthlyQuota": monthlyQuota,
		"createdAt":    time.Now().UTC().Format(time.RFC3339),
	}

	sendSuccessResponse(c, http.StatusCreated, map[string]interface{}{
		"service": serviceData,
		"message": "Service created successfully",
	})
}

// handleAPwhyServicesPatch updates an existing API service
func handleAPwhyServicesPatch(c *gin.Context) {
	serviceID := c.Param("id")

	var input struct {
		Enabled *bool `json:"enabled"`
	}

	if err := c.ShouldBindJSON(&input); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{
			"ok":    false,
			"error": "Invalid input: " + err.Error(),
		})
		return
	}

	db := c.MustGet("db").(*database.DB)

	if input.Enabled != nil {
		_, err := db.ExecContext(context.Background(),
			"UPDATE api_services SET enabled = $1, updated_at = NOW() WHERE id = $2",
			*input.Enabled, serviceID)

		if err != nil {
			c.JSON(http.StatusInternalServerError, gin.H{
				"ok":    false,
				"error": "Failed to update service: " + err.Error(),
			})
			return
		}
	}

	c.JSON(http.StatusOK, gin.H{
		"ok":   true,
		"data": gin.H{"id": serviceID, "updated": true},
	})
}

// handleAPwhyKeysList returns a list of API keys
func handleAPwhyKeysList(c *gin.Context) {
	db := c.MustGet("db").(*database.DB)

	rows, err := db.QueryContext(context.Background(), `
		SELECT id, name, key_prefix, plan, enabled, rpm_limit, monthly_quota, created_at, updated_at
		FROM api_keys 
		ORDER BY created_at DESC
	`)
	if err != nil {
		c.JSON(http.StatusOK, gin.H{
			"ok":   true,
			"data": []interface{}{},
		})
		return
	}
	defer rows.Close()

	var keys []map[string]interface{}
	for rows.Next() {
		var id, name, keyPrefix, plan, createdAt, updatedAt string
		var enabled bool
		var rpmLimit, monthlyQuota sql.NullInt64

		err := rows.Scan(&id, &name, &keyPrefix, &plan, &enabled, &rpmLimit, &monthlyQuota, &createdAt, &updatedAt)
		if err != nil {
			continue
		}

		key := map[string]interface{}{
			"id":        id,
			"name":      name,
			"keyPrefix": keyPrefix,
			"plan":      plan,
			"enabled":   enabled,
			"createdAt": createdAt,
			"updatedAt": updatedAt,
		}

		if rpmLimit.Valid {
			key["rpmLimit"] = rpmLimit.Int64
		}
		if monthlyQuota.Valid {
			key["monthlyQuota"] = monthlyQuota.Int64
		}

		keys = append(keys, key)
	}

	c.JSON(http.StatusOK, gin.H{
		"ok":   true,
		"data": keys,
	})
}

// handleAPwhyKeysCreate creates a new API key
func handleAPwhyKeysCreate(c *gin.Context) {
	var input struct {
		Name string `json:"name" binding:"required"`
		Plan string `json:"plan"`
	}

	if err := c.ShouldBindJSON(&input); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{
			"ok":    false,
			"error": "Invalid input: " + err.Error(),
		})
		return
	}

	if input.Plan == "" {
		input.Plan = "free"
	}

	db := c.MustGet("db").(*database.DB)

	// Generate API key and hash
	apiKey, err := generateAPIKey()
	if err != nil {
		sendErrorResponse(c, http.StatusInternalServerError, "GENERATION_ERROR",
			"Failed to generate API key", err.Error())
		return
	}

	keyHash, err := hashAPIKey(apiKey)
	if err != nil {
		sendErrorResponse(c, http.StatusInternalServerError, "HASH_ERROR",
			"Failed to hash API key", err.Error())
		return
	}

	// Generate ID and prefix
	id, err := generateServiceID()
	if err != nil {
		sendErrorResponse(c, http.StatusInternalServerError, "GENERATION_ERROR",
			"Failed to generate key ID", err.Error())
		return
	}

	keyPrefix := apiKey[:8]

	// Set default limits based on plan
	var rpmLimit, monthlyQuota int
	switch input.Plan {
	case "free":
		rpmLimit, monthlyQuota = 60, 1000
	case "pro":
		rpmLimit, monthlyQuota = 600, 50000
	case "business":
		rpmLimit, monthlyQuota = 3000, 300000
	default:
		rpmLimit, monthlyQuota = 60, 1000
	}

	// Insert key into database
	_, err = db.ExecContext(context.Background(), `
		INSERT INTO api_keys (
			id, name, key_hash, key_prefix, plan, allowed_service_ids,
			enabled, rpm_limit, monthly_quota, created_at, updated_at
		) VALUES ($1, $2, $3, $4, $5, '[]', true, $6, $7, NOW(), NOW())
	`, id, input.Name, keyHash, keyPrefix, input.Plan, rpmLimit, monthlyQuota)

	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{
			"ok":    false,
			"error": "Failed to create key: " + err.Error(),
		})
		return
	}

	c.JSON(http.StatusCreated, gin.H{
		"ok": true,
		"data": gin.H{
			"id":           id,
			"name":         input.Name,
			"plan":         input.Plan,
			"key":          apiKey, // Only return the actual key once
			"keyPrefix":    keyPrefix,
			"enabled":      true,
			"rpmLimit":     rpmLimit,
			"monthlyQuota": monthlyQuota,
		},
	})
}

// handleAPwhyKeysPatch updates an existing API key
func handleAPwhyKeysPatch(c *gin.Context) {
	keyID := c.Param("id")

	var input struct {
		Enabled *bool `json:"enabled"`
	}

	if err := c.ShouldBindJSON(&input); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{
			"ok":    false,
			"error": "Invalid input: " + err.Error(),
		})
		return
	}

	db := c.MustGet("db").(*database.DB)

	if input.Enabled != nil {
		_, err := db.ExecContext(context.Background(),
			"UPDATE api_keys SET enabled = $1, updated_at = NOW() WHERE id = $2",
			*input.Enabled, keyID)

		if err != nil {
			c.JSON(http.StatusInternalServerError, gin.H{
				"ok":    false,
				"error": "Failed to update key: " + err.Error(),
			})
			return
		}
	}

	c.JSON(http.StatusOK, gin.H{
		"ok":   true,
		"data": gin.H{"id": keyID, "updated": true},
	})
}

// handleAPwhyAnalyticsOps returns operational analytics
func handleAPwhyAnalyticsOps(c *gin.Context) {
	db := c.MustGet("db").(*database.DB)

	// Get counts from database
	var totalServices, totalKeys, totalUsers int

	if err := db.QueryRowContext(context.Background(), "SELECT COUNT(*) FROM api_services").Scan(&totalServices); err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{
			"ok":    false,
			"error": "Failed to count services: " + err.Error(),
		})
		return
	}
	if err := db.QueryRowContext(context.Background(), "SELECT COUNT(*) FROM api_keys").Scan(&totalKeys); err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{
			"ok":    false,
			"error": "Failed to count api keys: " + err.Error(),
		})
		return
	}
	if err := db.QueryRowContext(context.Background(), "SELECT COUNT(*) FROM users").Scan(&totalUsers); err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{
			"ok":    false,
			"error": "Failed to count users: " + err.Error(),
		})
		return
	}

	var totalRequests int
	if err := db.QueryRowContext(context.Background(), "SELECT COALESCE(SUM(request_count), 0) FROM usage_counters").Scan(&totalRequests); err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{
			"ok":    false,
			"error": "Failed to aggregate request counters: " + err.Error(),
		})
		return
	}

	var requestsToday int
	if err := db.QueryRowContext(context.Background(), `
		SELECT COALESCE(SUM(value), 0)::int
		FROM metrics_timeseries
		WHERE metric = 'request_total' AND occurred_at >= DATE_TRUNC('day', NOW())
	`).Scan(&requestsToday); err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{
			"ok":    false,
			"error": "Failed to aggregate today's requests: " + err.Error(),
		})
		return
	}

	var requestsThisMonth int
	if err := db.QueryRowContext(context.Background(), `
		SELECT COALESCE(SUM(value), 0)::int
		FROM metrics_timeseries
		WHERE metric = 'request_total' AND occurred_at >= DATE_TRUNC('month', NOW())
	`).Scan(&requestsThisMonth); err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{
			"ok":    false,
			"error": "Failed to aggregate monthly requests: " + err.Error(),
		})
		return
	}

	c.JSON(http.StatusOK, gin.H{
		"ok": true,
		"data": gin.H{
			"total_requests":      totalRequests,
			"total_services":      totalServices,
			"total_keys":          totalKeys,
			"total_users":         totalUsers,
			"requests_today":      requestsToday,
			"requests_this_month": requestsThisMonth,
		},
	})
}

// handleAPwhyAnalyticsTraffic returns traffic analytics
func handleAPwhyAnalyticsTraffic(c *gin.Context) {
	db := c.MustGet("db").(*database.DB)

	topServices := make([]map[string]interface{}, 0)
	serviceRows, err := db.QueryContext(context.Background(), `
		SELECT s.id, s.name, COALESCE(SUM(u.request_count), 0) AS total_requests
		FROM api_services s
		LEFT JOIN usage_counters u ON u.service_id = s.id
		GROUP BY s.id, s.name
		ORDER BY total_requests DESC, s.name ASC
		LIMIT 10
	`)
	if err == nil {
		defer serviceRows.Close()
		for serviceRows.Next() {
			var serviceID, serviceName string
			var totalRequests int
			if scanErr := serviceRows.Scan(&serviceID, &serviceName, &totalRequests); scanErr == nil {
				topServices = append(topServices, map[string]interface{}{
					"service_id": serviceID,
					"name":       serviceName,
					"requests":   totalRequests,
				})
			}
		}
	}

	requestsByDay := make([]map[string]interface{}, 0)
	trafficRows, err := db.QueryContext(context.Background(), `
		SELECT TO_CHAR(DATE_TRUNC('day', occurred_at), 'YYYY-MM-DD') AS day_bucket, COUNT(*) AS total
		FROM metrics_timeseries
		WHERE metric = 'request_total' AND occurred_at >= NOW() - INTERVAL '7 days'
		GROUP BY DATE_TRUNC('day', occurred_at)
		ORDER BY DATE_TRUNC('day', occurred_at) ASC
	`)
	if err == nil {
		defer trafficRows.Close()
		for trafficRows.Next() {
			var day string
			var count int
			if scanErr := trafficRows.Scan(&day, &count); scanErr == nil {
				requestsByDay = append(requestsByDay, map[string]interface{}{
					"day":      day,
					"requests": count,
				})
			}
		}
	}

	statusCodes := make([]map[string]interface{}, 0)
	statusRows, err := db.QueryContext(context.Background(), `
		SELECT COALESCE(http_status, 0) AS status_code, COALESCE(SUM(count), 0) AS total
		FROM incident_events
		WHERE occurred_at >= NOW() - INTERVAL '7 days'
		GROUP BY COALESCE(http_status, 0)
		ORDER BY total DESC, status_code ASC
	`)
	if err == nil {
		defer statusRows.Close()
		for statusRows.Next() {
			var code int
			var total int
			if scanErr := statusRows.Scan(&code, &total); scanErr == nil {
				statusCodes = append(statusCodes, map[string]interface{}{
					"status_code": code,
					"count":       total,
				})
			}
		}
	}

	clientEvents := make([]map[string]interface{}, 0)
	eventRows, err := db.QueryContext(context.Background(), `
		SELECT COALESCE((labels_json::jsonb ->> 'path'), 'unknown') AS path, COUNT(*) AS total
		FROM metrics_timeseries
		WHERE metric = 'client_event' AND occurred_at >= NOW() - INTERVAL '7 days'
		GROUP BY COALESCE((labels_json::jsonb ->> 'path'), 'unknown')
		ORDER BY total DESC, path ASC
		LIMIT 20
	`)
	if err == nil {
		defer eventRows.Close()
		for eventRows.Next() {
			var path string
			var total int
			if scanErr := eventRows.Scan(&path, &total); scanErr == nil {
				clientEvents = append(clientEvents, map[string]interface{}{
					"path":  path,
					"count": total,
				})
			}
		}
	}

	c.JSON(http.StatusOK, gin.H{
		"ok": true,
		"data": gin.H{
			"top_services":    topServices,
			"requests_by_day": requestsByDay,
			"status_codes":    statusCodes,
			"client_events":   clientEvents,
		},
	})
}
