package iace

import (
	"context"
	"encoding/json"
	"fmt"
	"time"

	"github.com/google/uuid"
	"github.com/jackc/pgx/v5"
)

// ============================================================================
// Production Line CRUD Operations
// ============================================================================

// CreateProductionLine creates a new production line for a tenant
func (s *Store) CreateProductionLine(ctx context.Context, tenantID uuid.UUID, name, description string) (*ProductionLine, error) {
	line := &ProductionLine{
		ID:          uuid.New(),
		TenantID:    tenantID,
		Name:        name,
		Description: description,
		CreatedAt:   time.Now().UTC(),
		UpdatedAt:   time.Now().UTC(),
	}

	_, err := s.pool.Exec(ctx, `
		INSERT INTO iace_production_lines (
			id, tenant_id, name, description, layout, created_at, updated_at
		) VALUES ($1, $2, $3, $4, $5, $6, $7)
	`, line.ID, line.TenantID, line.Name, line.Description, json.RawMessage("{}"), line.CreatedAt, line.UpdatedAt)
	if err != nil {
		return nil, fmt.Errorf("create production line: %w", err)
	}

	return line, nil
}

// ListProductionLines lists all production lines for a tenant
func (s *Store) ListProductionLines(ctx context.Context, tenantID uuid.UUID) ([]ProductionLine, error) {
	rows, err := s.pool.Query(ctx, `
		SELECT id, tenant_id, name, description, layout, created_at, updated_at
		FROM iace_production_lines
		WHERE tenant_id = $1
		ORDER BY created_at DESC
	`, tenantID)
	if err != nil {
		return nil, fmt.Errorf("list production lines: %w", err)
	}
	defer rows.Close()

	var lines []ProductionLine
	for rows.Next() {
		var l ProductionLine
		var layout []byte
		err := rows.Scan(&l.ID, &l.TenantID, &l.Name, &l.Description, &layout, &l.CreatedAt, &l.UpdatedAt)
		if err != nil {
			return nil, fmt.Errorf("list production lines scan: %w", err)
		}
		json.Unmarshal(layout, &l.Layout)
		lines = append(lines, l)
	}

	return lines, nil
}

// GetProductionLine retrieves a production line by ID
func (s *Store) GetProductionLine(ctx context.Context, id uuid.UUID) (*ProductionLine, error) {
	var l ProductionLine
	var layout []byte

	err := s.pool.QueryRow(ctx, `
		SELECT id, tenant_id, name, description, layout, created_at, updated_at
		FROM iace_production_lines WHERE id = $1
	`, id).Scan(&l.ID, &l.TenantID, &l.Name, &l.Description, &layout, &l.CreatedAt, &l.UpdatedAt)
	if err == pgx.ErrNoRows {
		return nil, nil
	}
	if err != nil {
		return nil, fmt.Errorf("get production line: %w", err)
	}

	json.Unmarshal(layout, &l.Layout)
	return &l, nil
}

// ============================================================================
// Station CRUD Operations
// ============================================================================

// AddStation adds a station (project reference) to a production line
func (s *Store) AddStation(ctx context.Context, lineID, projectID uuid.UUID, stationType, label string, sortOrder int) (*ProductionLineStation, error) {
	st := &ProductionLineStation{
		ID:           uuid.New(),
		LineID:       lineID,
		ProjectID:    projectID,
		StationType:  stationType,
		StationLabel: label,
		SortOrder:    sortOrder,
		CreatedAt:    time.Now().UTC(),
	}

	if st.StationType == "" {
		st.StationType = "assembly"
	}

	_, err := s.pool.Exec(ctx, `
		INSERT INTO iace_production_line_stations (
			id, line_id, project_id, station_type, station_label,
			sort_order, position_x, position_y, metadata, created_at
		) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)
	`, st.ID, st.LineID, st.ProjectID, st.StationType, st.StationLabel,
		st.SortOrder, st.PositionX, st.PositionY, json.RawMessage("{}"), st.CreatedAt)
	if err != nil {
		return nil, fmt.Errorf("add station: %w", err)
	}

	return st, nil
}

// ListStations lists all stations for a production line ordered by sort_order
func (s *Store) ListStations(ctx context.Context, lineID uuid.UUID) ([]ProductionLineStation, error) {
	rows, err := s.pool.Query(ctx, `
		SELECT id, line_id, project_id, station_type, station_label,
			sort_order, position_x, position_y, metadata, created_at
		FROM iace_production_line_stations
		WHERE line_id = $1
		ORDER BY sort_order ASC, created_at ASC
	`, lineID)
	if err != nil {
		return nil, fmt.Errorf("list stations: %w", err)
	}
	defer rows.Close()

	var stations []ProductionLineStation
	for rows.Next() {
		var st ProductionLineStation
		var metadata []byte
		err := rows.Scan(
			&st.ID, &st.LineID, &st.ProjectID, &st.StationType, &st.StationLabel,
			&st.SortOrder, &st.PositionX, &st.PositionY, &metadata, &st.CreatedAt,
		)
		if err != nil {
			return nil, fmt.Errorf("list stations scan: %w", err)
		}
		json.Unmarshal(metadata, &st.Metadata)
		stations = append(stations, st)
	}

	return stations, nil
}

// RemoveStation deletes a station from a production line
func (s *Store) RemoveStation(ctx context.Context, stationID uuid.UUID) error {
	_, err := s.pool.Exec(ctx, `DELETE FROM iace_production_line_stations WHERE id = $1`, stationID)
	if err != nil {
		return fmt.Errorf("remove station: %w", err)
	}
	return nil
}

// ============================================================================
// Dashboard Aggregation
// ============================================================================

// GetLineDashboard builds the full dashboard for a production line by aggregating
// risk data from each station's underlying IACE project.
func (s *Store) GetLineDashboard(ctx context.Context, lineID uuid.UUID) (*LineDashboard, error) {
	line, err := s.GetProductionLine(ctx, lineID)
	if err != nil {
		return nil, fmt.Errorf("get line dashboard - load line: %w", err)
	}
	if line == nil {
		return nil, nil
	}

	stations, err := s.ListStations(ctx, lineID)
	if err != nil {
		return nil, fmt.Errorf("get line dashboard - list stations: %w", err)
	}

	aggregate := map[string]int{
		"total_hazards":     0,
		"total_mitigations": 0,
		"critical":          0,
		"high":              0,
		"medium":            0,
		"low":               0,
		"negligible":        0,
	}

	var dashboards []StationDashboard
	for _, st := range stations {
		sd, err := s.buildStationDashboard(ctx, st)
		if err != nil {
			return nil, fmt.Errorf("get line dashboard - station %s: %w", st.ID, err)
		}
		dashboards = append(dashboards, *sd)

		// Accumulate aggregates
		aggregate["total_hazards"] += sd.HazardCount
		aggregate["total_mitigations"] += sd.MitigationCount
		for level, count := range sd.RiskSummary {
			aggregate[level] += count
		}
	}

	// Build transfers: linear chain station[i] -> station[i+1]
	var transfers []TransferInfo
	for i := 0; i < len(dashboards)-1; i++ {
		transfers = append(transfers, TransferInfo{
			FromStation: dashboards[i].Station.SortOrder,
			ToStation:   dashboards[i+1].Station.SortOrder,
			Type:        "material_flow",
			Label:       fmt.Sprintf("%s -> %s", stationDisplayName(dashboards[i]), stationDisplayName(dashboards[i+1])),
		})
	}

	return &LineDashboard{
		Line:      *line,
		Stations:  dashboards,
		Transfers: transfers,
		Aggregate: aggregate,
	}, nil
}

// buildStationDashboard loads project data for a single station and computes
// risk metrics including SIL/PL maximums.
func (s *Store) buildStationDashboard(ctx context.Context, st ProductionLineStation) (*StationDashboard, error) {
	project, err := s.GetProject(ctx, st.ProjectID)
	if err != nil {
		return nil, fmt.Errorf("load project: %w", err)
	}

	sd := &StationDashboard{
		Station:     st,
		RiskSummary: map[string]int{},
		SILMax:      "none",
		PLMax:       "a",
	}

	if project == nil {
		sd.Status = "missing"
		return sd, nil
	}

	sd.ProjectName = project.MachineName
	sd.MachineType = project.MachineType
	sd.Status = string(project.Status)
	sd.CompletenessPct = int(project.CompletenessScore)

	hazards, _ := s.ListHazards(ctx, st.ProjectID)
	sd.HazardCount = len(hazards)

	mitigations, _ := s.ListMitigationsByProject(ctx, st.ProjectID)
	sd.MitigationCount = len(mitigations)

	// Batch-load all latest assessments in ONE query (not N+1)
	latestByHazard, _ := s.GetLatestAssessmentsByProject(ctx, st.ProjectID)

	for _, h := range hazards {
		latest, ok := latestByHazard[h.ID]
		if !ok {
			continue
		}

		levelKey := string(latest.RiskLevel)
		sd.RiskSummary[levelKey]++

		// Derive SIL/PL from assessment values and track maximums
		silPL := CalculateSILPL(latest.Severity, latest.Exposure, latest.Probability)
		if plRank(silPL.RecommendedPL) > plRank(sd.PLMax) {
			sd.PLMax = silPL.RecommendedPL
		}
		if silRank(silPL.RecommendedSIL) > silRank(sd.SILMax) {
			sd.SILMax = silPL.RecommendedSIL
		}
	}

	return sd, nil
}

// stationDisplayName returns a human-readable label for a station dashboard entry
func stationDisplayName(sd StationDashboard) string {
	if sd.Station.StationLabel != "" {
		return sd.Station.StationLabel
	}
	if sd.ProjectName != "" {
		return sd.ProjectName
	}
	return fmt.Sprintf("Station %d", sd.Station.SortOrder)
}

// plRank returns a numeric rank for PL comparison (a=1 ... e=5)
func plRank(pl string) int {
	switch pl {
	case "a":
		return 1
	case "b":
		return 2
	case "c":
		return 3
	case "d":
		return 4
	case "e":
		return 5
	default:
		return 0
	}
}

// silRank returns a numeric rank for SIL comparison
func silRank(sil string) int {
	switch sil {
	case "none":
		return 0
	case "SIL 1":
		return 1
	case "SIL 2":
		return 2
	case "SIL 3":
		return 3
	default:
		return 0
	}
}
