package handler

import (
	"net/http"
	"strconv"
	"strings"
	"time"

	"github.com/gin-gonic/gin"

	vulnmodel "vul-backend/internal/modules/vuln/model"
	vulnservice "vul-backend/internal/modules/vuln/service"
	"vul-backend/internal/pkg/response"
)

type Handler struct {
	service *vulnservice.Service
}

func New(service *vulnservice.Service) *Handler {
	return &Handler{service: service}
}

func (h *Handler) RegisterRoutes(rg *gin.RouterGroup) {
	group := rg.Group("/vulnerabilities")
	group.GET("", h.List)
	group.POST("", h.Create)
	group.GET("/config", h.Config)
}

func (h *Handler) List(c *gin.Context) {
	page := parseIntDefault(c.DefaultQuery("page", "1"), 1)
	pageSize := parseIntDefault(c.DefaultQuery("page_size", "20"), 20)
	statuses := getMultiValueParam(c, "status")
	severities := getMultiValueParam(c, "severity")

	query := vulnservice.ListQuery{
		Page:              page,
		PageSize:          pageSize,
		Statuses:          toStatuses(statuses),
		Severities:        toSeverities(severities),
		Application:       c.Query("application"),
		BusinessDept:      c.Query("business_dept"),
		DiscoveryPhase:    c.Query("discovery_phase"),
		DiscoveryChannel:  c.Query("discovery_channel"),
		NetworkEnv:        c.Query("network_env"),
		Keyword:           c.Query("search"),
		Tags:              getMultiValueParam(c, "tags"),
		DependencyName:    c.Query("dependency_name"),
		DependencyVersion: c.Query("dependency_version"),
		SortBy:            c.Query("sort_by"),
		SortOrder:         c.Query("sort_order"),
	}

	if ownerID := parseUint64Pointer(c.Query("owner_id")); ownerID != nil {
		query.OwnerID = ownerID
	}
	if submitterID := parseUint64Pointer(c.Query("submitter_id")); submitterID != nil {
		query.SubmitterID = submitterID
	}
	if batchGroupID := parseUint64Pointer(c.Query("batch_group_id")); batchGroupID != nil {
		query.BatchGroupID = batchGroupID
	}
	switch strings.ToLower(c.Query("batch_scope")) {
	case "batched":
		value := true
		query.HasBatchGroup = &value
	case "single":
		value := false
		query.HasBatchGroup = &value
	}

	if hasPOC := parseBoolPointer(c.Query("has_poc")); hasPOC != nil {
		query.HasPOC = hasPOC
	}
	if isOverdue := parseBoolPointer(c.Query("is_overdue")); isOverdue != nil {
		query.IsOverdue = isOverdue
	}
	if isThirdParty := parseBoolPointer(c.Query("is_third_party")); isThirdParty != nil {
		query.IsThirdParty = isThirdParty
	}

	if from := parseTimePointer(c.Query("submitted_from")); from != nil {
		query.SubmittedFrom = from
	}
	if to := parseTimePointer(c.Query("submitted_to")); to != nil {
		query.SubmittedTo = to
	}

	result, err := h.service.List(c.Request.Context(), query)
	if err != nil {
		response.Error(c, http.StatusInternalServerError, err.Error())
		return
	}
	effectivePage := page
	if result.Page > 0 {
		effectivePage = result.Page
	}
	effectiveSize := pageSize
	if result.PageSize > 0 {
		effectiveSize = result.PageSize
	}
	items := make([]listItem, len(result.Items))
	for i := range result.Items {
		items[i] = listItem{
			Vulnerability: result.Items[i],
			Actions:       h.availableActions(&result.Items[i]),
		}
	}

	response.Success(c, response.PaginatedResponse{
		Items:      items,
		Total:      result.Total,
		Page:       effectivePage,
		PageSize:   effectiveSize,
		TotalPages: int((result.Total + int64(effectiveSize) - 1) / int64(effectiveSize)),
	})
}

func (h *Handler) Config(c *gin.Context) {
	result, err := h.service.GetConfiguration(c.Request.Context())
	if err != nil {
		response.Error(c, http.StatusInternalServerError, err.Error())
		return
	}
	response.Success(c, result)
}

func (h *Handler) Create(c *gin.Context) {
	var payload struct {
		Title               string                        `json:"title" binding:"required"`
		SeverityLevel       vulnmodel.SeverityLevel       `json:"severity_level" binding:"required"`
		Status              vulnmodel.VulnerabilityStatus `json:"status" binding:"required"`
		Application         string                        `json:"application"`
		BusinessDept        string                        `json:"business_dept"`
		VulnerabilityType   string                        `json:"vulnerability_type"`
		DiscoveryChannel    string                        `json:"discovery_channel"`
		DiscoveryPhase      string                        `json:"discovery_phase"`
		NetworkEnv          string                        `json:"network_env"`
		IsThirdParty        bool                          `json:"is_third_party"`
		HasPOCVerification  bool                          `json:"has_poc_verification"`
		AffectedAssetsCount int                           `json:"affected_assets_count"`
		Tags                []string                      `json:"tags"`
		DependencyName      string                        `json:"dependency_name"`
		DependencyVersion   string                        `json:"dependency_version"`
		BatchGroupID        *uint64                       `json:"batch_group_id"`
		OwnerID             *uint64                       `json:"owner_id"`
		SubmitterID         uint64                        `json:"submitter_id" binding:"required"`
		InterfacePath       string                        `json:"interface_path"`
		Source              string                        `json:"source"`
		Description         string                        `json:"description"`
		VerificationSteps   string                        `json:"verification_steps"`
		POCText             string                        `json:"poc_text"`
		SLADeadline         *time.Time                    `json:"sla_deadline"`
		SubmittedAt         *time.Time                    `json:"submitted_at"`
	}
	if err := c.ShouldBindJSON(&payload); err != nil {
		response.Error(c, http.StatusBadRequest, err.Error())
		return
	}

	now := time.Now().UTC()
	submittedAt := payload.SubmittedAt
	if submittedAt == nil {
		submittedAt = &now
	}

	slaDeadline := payload.SLADeadline
	if slaDeadline == nil {
		defaultDeadline := submittedAt.Add(defaultSLADuration(payload.SeverityLevel))
		slaDeadline = &defaultDeadline
	}

	vuln := &vulnmodel.Vulnerability{
		Title:               payload.Title,
		SeverityLevel:       payload.SeverityLevel,
		Status:              payload.Status,
		Application:         payload.Application,
		BusinessDept:        payload.BusinessDept,
		VulnerabilityType:   payload.VulnerabilityType,
		DiscoveryChannel:    payload.DiscoveryChannel,
		DiscoveryPhase:      payload.DiscoveryPhase,
		NetworkEnv:          payload.NetworkEnv,
		IsThirdParty:        payload.IsThirdParty,
		HasPOCVerification:  payload.HasPOCVerification,
		AffectedAssetsCount: payload.AffectedAssetsCount,
		Tags:                vulnmodel.StringArray(payload.Tags),
		DependencyName:      payload.DependencyName,
		DependencyVersion:   payload.DependencyVersion,
		BatchGroupID:        payload.BatchGroupID,
		OwnerID:             payload.OwnerID,
		SubmitterID:         payload.SubmitterID,
		InterfacePath:       payload.InterfacePath,
		Source:              payload.Source,
		Description:         payload.Description,
		VerificationSteps:   payload.VerificationSteps,
		POCText:             payload.POCText,
		SubmittedAt:         *submittedAt,
		SLADeadline:         *slaDeadline,
		IsOverdue:           false,
	}

	if err := h.service.Create(c.Request.Context(), vuln); err != nil {
		response.Error(c, http.StatusInternalServerError, err.Error())
		return
	}
	response.Success(c, vuln)
}

func parseCSV(value string) []string {
	if value == "" {
		return nil
	}
	parts := strings.Split(value, ",")
	result := make([]string, 0, len(parts))
	for _, part := range parts {
		if trimmed := strings.TrimSpace(part); trimmed != "" {
			result = append(result, trimmed)
		}
	}
	return result
}

func getMultiValueParam(c *gin.Context, key string) []string {
	values := c.QueryArray(key)
	if len(values) == 0 {
		return parseCSV(c.Query(key))
	}
	var combined []string
	for _, value := range values {
		combined = append(combined, parseCSV(value)...)
	}
	return combined
}

func parseIntDefault(value string, fallback int) int {
	if value == "" {
		return fallback
	}
	if n, err := strconv.Atoi(value); err == nil && n > 0 {
		return n
	}
	return fallback
}

func parseUint64Pointer(value string) *uint64 {
	if value == "" {
		return nil
	}
	n, err := strconv.ParseUint(value, 10, 64)
	if err != nil {
		return nil
	}
	u := uint64(n)
	return &u
}

func parseBoolPointer(value string) *bool {
	if value == "" {
		return nil
	}
	switch strings.ToLower(value) {
	case "true", "1", "yes":
		v := true
		return &v
	case "false", "0", "no":
		v := false
		return &v
	default:
		return nil
	}
}

func parseTimePointer(value string) *time.Time {
	if value == "" {
		return nil
	}
	layouts := []string{time.RFC3339, "2006-01-02"}
	for _, layout := range layouts {
		if t, err := time.Parse(layout, value); err == nil {
			return &t
		}
	}
	return nil
}

func toStatuses(values []string) []vulnmodel.VulnerabilityStatus {
	if len(values) == 0 {
		return nil
	}
	statuses := make([]vulnmodel.VulnerabilityStatus, 0, len(values))
	for _, v := range values {
		statuses = append(statuses, vulnmodel.VulnerabilityStatus(v))
	}
	return statuses
}

func toSeverities(values []string) []vulnmodel.SeverityLevel {
	if len(values) == 0 {
		return nil
	}
	severities := make([]vulnmodel.SeverityLevel, 0, len(values))
	for _, v := range values {
		severities = append(severities, vulnmodel.SeverityLevel(v))
	}
	return severities
}

func defaultSLADuration(severity vulnmodel.SeverityLevel) time.Duration {
	switch severity {
	case vulnmodel.SeverityCritical:
		return 24 * time.Hour
	case vulnmodel.SeverityHigh:
		return 72 * time.Hour
	case vulnmodel.SeverityMedium:
		return 7 * 24 * time.Hour
	default:
		return 14 * 24 * time.Hour
	}
}

type listItem struct {
	vulnmodel.Vulnerability
	Actions []string `json:"actions"`
}

func (h *Handler) availableActions(v *vulnmodel.Vulnerability) []string {
	return []string{"view", "edit", "process"}
}
