package qc

import (
	"encoding/json"
	"fmt"
	"net/http"
	"verofy-backend/models"

	"github.com/gin-gonic/gin"
	"gorm.io/gorm"
)

type SingleSpanResult struct {
	SegmentID    int                    `json:"segment_id"`
	SegmentName  string                 `json:"segment_name"`
	Type         string                 `json:"type"`
	VertexCount  int                    `json:"vertex_count"`
	IsValid      bool                   `json:"is_valid"`
	ErrorMessage string                 `json:"error_message,omitempty"`
	Geometry     map[string]interface{} `json:"geometry,omitempty"`
}

type SingleSpanSummary struct {
	TotalAerialSegments int                `json:"total_aerial_segments"`
	ValidSegments       int                `json:"valid_segments"`
	InvalidSegments     int                `json:"invalid_segments"`
	PassRate            float64            `json:"pass_rate"`
	Results             []SingleSpanResult `json:"results"`
}

func SingleSpanRoute(router *gin.Engine, db *gorm.DB, schema, segmentTable, mapIDCol, zoneCol, idCol, qcFlagCol string) {
	// Full single-span summary endpoint
	router.GET("/api/qc/single-span", func(c *gin.Context) {
		mapID := c.Query("map_id")
		zone := c.Query("zone")
		if mapID == "" || zone == "" {
			c.JSON(http.StatusBadRequest, gin.H{"error": "map_id and zone are required"})
			return
		}

		summary, err := CheckSingleSpan(db, mapID, zone, schema, segmentTable, mapIDCol, zoneCol, idCol, qcFlagCol)
		if err != nil {
			c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
			return
		}

		c.JSON(http.StatusOK, summary)
	})

	// Invalid segments only endpoint
	router.GET("/api/qc/single-span/invalid", func(c *gin.Context) {
		mapID := c.Query("map_id")
		zone := c.Query("zone")
		if mapID == "" || zone == "" {
			c.JSON(http.StatusBadRequest, gin.H{"error": "map_id and zone are required"})
			return
		}

		invalid, err := GetInvalidSingleSpanSegments(db, mapID, zone, schema, segmentTable, mapIDCol, zoneCol, idCol, qcFlagCol)
		if err != nil {
			c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
			return
		}

		c.JSON(http.StatusOK, gin.H{
			"invalid_segments": invalid,
			"count":            len(invalid),
		})
	})
}

// CheckSingleSpan validates that aerial segments have exactly 2 vertices
func CheckSingleSpan(db *gorm.DB, mapID, zone, schema, segmentTable, mapIDCol, zoneCol, idCol, qcFlagCol string) (*SingleSpanSummary, error) {
	var segments []models.SegmentGeoJSON
	table := fmt.Sprintf("%s.%s", schema, segmentTable)

	// Query aerial segments using the same pattern as graph_connect.go
	err := db.Table(table).
		Select(fmt.Sprintf("id_0, %s, segment_type, segment_status, %s, protection_status, %s, ST_AsGeoJSON(ST_Transform(geom, 4326))::json AS geometry", mapIDCol, idCol, qcFlagCol)).
		Where(fmt.Sprintf("%s = ? AND %s = ? AND LOWER(segment_type) = ?", mapIDCol, zoneCol), mapID, zone, "aerial").
		Find(&segments).Error

	if err != nil {
		return nil, fmt.Errorf("failed to fetch aerial segments: %w", err)
	}

	summary := &SingleSpanSummary{
		TotalAerialSegments: len(segments),
		Results:             make([]SingleSpanResult, 0, len(segments)),
	}

	for _, segment := range segments {
		result := validateSegmentSpan(segment)
		summary.Results = append(summary.Results, result)

		if result.IsValid {
			summary.ValidSegments++
		} else {
			summary.InvalidSegments++
		}
	}

	// Calculate pass rate
	if summary.TotalAerialSegments > 0 {
		summary.PassRate = float64(summary.ValidSegments) / float64(summary.TotalAerialSegments) * 100
	}

	return summary, nil
}

// validateSegmentSpan checks if a segment has exactly 2 vertices
func validateSegmentSpan(segment models.SegmentGeoJSON) SingleSpanResult {
	result := SingleSpanResult{
		SegmentID: int(segment.ID),
		Type:      segment.SegmentType,
		IsValid:   false,
	}

	// Parse the geometry to count vertices
	if len(segment.Geometry) > 0 {
		vertexCount, geometry, err := countVerticesFromRawMessage(segment.Geometry)
		if err != nil {
			result.ErrorMessage = fmt.Sprintf("Failed to parse geometry: %v", err)
			return result
		}

		result.VertexCount = vertexCount
		result.Geometry = geometry

		// Aerial segments should have exactly 2 vertices (one span)
		if vertexCount == 2 {
			result.IsValid = true
		} else if vertexCount < 2 {
			result.ErrorMessage = fmt.Sprintf("Segment has only %d vertex(es), needs exactly 2 for a valid span", vertexCount)
		} else {
			result.ErrorMessage = fmt.Sprintf("Segment has %d vertices, should be exactly 2 for a single span (pole to pole)", vertexCount)
		}
	} else {
		result.ErrorMessage = "Segment has no geometry data"
	}

	return result
}

// countVerticesFromRawMessage parses json.RawMessage and counts vertices
func countVerticesFromRawMessage(geometryRaw json.RawMessage) (int, map[string]interface{}, error) {
	var geometry map[string]interface{}

	if err := json.Unmarshal(geometryRaw, &geometry); err != nil {
		return 0, nil, fmt.Errorf("invalid GeoJSON: %w", err)
	}

	geometryType, ok := geometry["type"].(string)
	if !ok {
		return 0, geometry, fmt.Errorf("missing or invalid geometry type")
	}

	coordinates, ok := geometry["coordinates"]
	if !ok {
		return 0, geometry, fmt.Errorf("missing coordinates")
	}

	var vertexCount int

	switch geometryType {
	case "LineString":
		// LineString coordinates are [[x,y], [x,y], ...]
		if coordArray, ok := coordinates.([]interface{}); ok {
			vertexCount = len(coordArray)
		}
	case "MultiLineString":
		// MultiLineString coordinates are [[[x,y], [x,y]], [[x,y], [x,y]]]
		if coordArrays, ok := coordinates.([]interface{}); ok {
			for _, coordArray := range coordArrays {
				if lineCoords, ok := coordArray.([]interface{}); ok {
					vertexCount += len(lineCoords)
				}
			}
		}
	default:
		return 0, geometry, fmt.Errorf("unsupported geometry type: %s", geometryType)
	}

	return vertexCount, geometry, nil
}

// GetInvalidSingleSpanSegments returns only the segments that failed the check
func GetInvalidSingleSpanSegments(db *gorm.DB, mapID, zone, schema, segmentTable, mapIDCol, zoneCol, idCol, qcFlagCol string) ([]SingleSpanResult, error) {
	summary, err := CheckSingleSpan(db, mapID, zone, schema, segmentTable, mapIDCol, zoneCol, idCol, qcFlagCol)
	if err != nil {
		return nil, err
	}

	var invalid []SingleSpanResult
	for _, result := range summary.Results {
		if !result.IsValid {
			invalid = append(invalid, result)
		}
	}

	return invalid, nil
}
