package iace

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

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


// ============================================================================
// Component CRUD Operations
// ============================================================================

// CreateComponent creates a new component within a project
func (s *Store) CreateComponent(ctx context.Context, req CreateComponentRequest) (*Component, error) {
	status := req.PresenceStatus
	if status == "" {
		status = PresencePresent
	}
	comp := &Component{
		ID:               uuid.New(),
		ProjectID:        req.ProjectID,
		ParentID:         req.ParentID,
		Name:             req.Name,
		ComponentType:    req.ComponentType,
		Version:          req.Version,
		Description:      req.Description,
		IsSafetyRelevant: req.IsSafetyRelevant,
		IsNetworked:      req.IsNetworked,
		CEMarked:         req.CEMarked,
		PresenceStatus:   status,
		CreatedAt:        time.Now().UTC(),
		UpdatedAt:        time.Now().UTC(),
	}

	_, err := s.pool.Exec(ctx, `
		INSERT INTO iace_components (
			id, project_id, parent_id, name, component_type,
			version, description, is_safety_relevant, is_networked,
			ce_marked, presence_status, metadata, sort_order, created_at, updated_at
		) VALUES (
			$1, $2, $3, $4, $5,
			$6, $7, $8, $9,
			$10, $11, $12, $13, $14, $15
		)
	`,
		comp.ID, comp.ProjectID, comp.ParentID, comp.Name, string(comp.ComponentType),
		comp.Version, comp.Description, comp.IsSafetyRelevant, comp.IsNetworked,
		comp.CEMarked, comp.PresenceStatus, comp.Metadata, comp.SortOrder, comp.CreatedAt, comp.UpdatedAt,
	)
	if err != nil {
		return nil, fmt.Errorf("create component: %w", err)
	}

	return comp, nil
}

// GetComponent retrieves a component by ID
func (s *Store) GetComponent(ctx context.Context, id uuid.UUID) (*Component, error) {
	var c Component
	var compType string
	var metadata []byte

	err := s.pool.QueryRow(ctx, `
		SELECT
			id, project_id, parent_id, name, component_type,
			version, description, is_safety_relevant, is_networked,
			ce_marked, presence_status, metadata, sort_order, created_at, updated_at
		FROM iace_components WHERE id = $1
	`, id).Scan(
		&c.ID, &c.ProjectID, &c.ParentID, &c.Name, &compType,
		&c.Version, &c.Description, &c.IsSafetyRelevant, &c.IsNetworked,
		&c.CEMarked, &c.PresenceStatus, &metadata, &c.SortOrder, &c.CreatedAt, &c.UpdatedAt,
	)
	if err == pgx.ErrNoRows {
		return nil, nil
	}
	if err != nil {
		return nil, fmt.Errorf("get component: %w", err)
	}

	c.ComponentType = ComponentType(compType)
	json.Unmarshal(metadata, &c.Metadata)

	return &c, nil
}

// ListComponents lists all components for a project
func (s *Store) ListComponents(ctx context.Context, projectID uuid.UUID) ([]Component, error) {
	rows, err := s.pool.Query(ctx, `
		SELECT
			id, project_id, parent_id, name, component_type,
			version, description, is_safety_relevant, is_networked,
			ce_marked, presence_status, metadata, sort_order, created_at, updated_at
		FROM iace_components WHERE project_id = $1
		ORDER BY sort_order ASC, created_at ASC
	`, projectID)
	if err != nil {
		return nil, fmt.Errorf("list components: %w", err)
	}
	defer rows.Close()

	var components []Component
	for rows.Next() {
		var c Component
		var compType string
		var metadata []byte

		err := rows.Scan(
			&c.ID, &c.ProjectID, &c.ParentID, &c.Name, &compType,
			&c.Version, &c.Description, &c.IsSafetyRelevant, &c.IsNetworked,
			&c.CEMarked, &c.PresenceStatus, &metadata, &c.SortOrder, &c.CreatedAt, &c.UpdatedAt,
		)
		if err != nil {
			return nil, fmt.Errorf("list components scan: %w", err)
		}

		c.ComponentType = ComponentType(compType)
		json.Unmarshal(metadata, &c.Metadata)

		components = append(components, c)
	}

	return components, nil
}

// UpdateComponent updates a component with a dynamic set of fields
func (s *Store) UpdateComponent(ctx context.Context, id uuid.UUID, updates map[string]interface{}) (*Component, error) {
	if len(updates) == 0 {
		return s.GetComponent(ctx, id)
	}

	query := "UPDATE iace_components SET updated_at = NOW()"
	args := []interface{}{id}
	argIdx := 2

	for key, val := range updates {
		switch key {
		case "name", "version", "description":
			query += fmt.Sprintf(", %s = $%d", key, argIdx)
			args = append(args, val)
			argIdx++
		case "component_type":
			query += fmt.Sprintf(", component_type = $%d", argIdx)
			args = append(args, val)
			argIdx++
		case "is_safety_relevant":
			query += fmt.Sprintf(", is_safety_relevant = $%d", argIdx)
			args = append(args, val)
			argIdx++
		case "is_networked":
			query += fmt.Sprintf(", is_networked = $%d", argIdx)
			args = append(args, val)
			argIdx++
		case "presence_status":
			query += fmt.Sprintf(", presence_status = $%d", argIdx)
			args = append(args, val)
			argIdx++
		case "ce_marked":
			query += fmt.Sprintf(", ce_marked = $%d", argIdx)
			args = append(args, val)
			argIdx++
		case "sort_order":
			query += fmt.Sprintf(", sort_order = $%d", argIdx)
			args = append(args, val)
			argIdx++
		case "metadata":
			metaJSON, _ := json.Marshal(val)
			query += fmt.Sprintf(", metadata = $%d", argIdx)
			args = append(args, metaJSON)
			argIdx++
		case "parent_id":
			query += fmt.Sprintf(", parent_id = $%d", argIdx)
			args = append(args, val)
			argIdx++
		}
	}

	query += " WHERE id = $1"

	_, err := s.pool.Exec(ctx, query, args...)
	if err != nil {
		return nil, fmt.Errorf("update component: %w", err)
	}

	return s.GetComponent(ctx, id)
}

// DeleteComponent deletes a component by ID
func (s *Store) DeleteComponent(ctx context.Context, id uuid.UUID) error {
	_, err := s.pool.Exec(ctx, "DELETE FROM iace_components WHERE id = $1", id)
	if err != nil {
		return fmt.Errorf("delete component: %w", err)
	}
	return nil
}

// ============================================================================
// Classification Operations
// ============================================================================

// UpsertClassification inserts or updates a regulatory classification for a project
func (s *Store) UpsertClassification(ctx context.Context, projectID uuid.UUID, regulation RegulationType, result string, riskLevel string, confidence float64, reasoning string, ragSources, requirements json.RawMessage) (*RegulatoryClassification, error) {
	id := uuid.New()
	now := time.Now().UTC()

	_, err := s.pool.Exec(ctx, `
		INSERT INTO iace_classifications (
			id, project_id, regulation, classification_result,
			risk_level, confidence, reasoning,
			rag_sources, requirements,
			created_at, updated_at
		) VALUES (
			$1, $2, $3, $4,
			$5, $6, $7,
			$8, $9,
			$10, $11
		)
		ON CONFLICT (project_id, regulation)
		DO UPDATE SET
			classification_result = EXCLUDED.classification_result,
			risk_level = EXCLUDED.risk_level,
			confidence = EXCLUDED.confidence,
			reasoning = EXCLUDED.reasoning,
			rag_sources = EXCLUDED.rag_sources,
			requirements = EXCLUDED.requirements,
			updated_at = EXCLUDED.updated_at
	`,
		id, projectID, string(regulation), result,
		riskLevel, confidence, reasoning,
		ragSources, requirements,
		now, now,
	)
	if err != nil {
		return nil, fmt.Errorf("upsert classification: %w", err)
	}

	// Retrieve the upserted row (may have kept the original ID on conflict)
	return s.getClassificationByProjectAndRegulation(ctx, projectID, regulation)
}

// getClassificationByProjectAndRegulation is a helper to fetch a single classification
func (s *Store) getClassificationByProjectAndRegulation(ctx context.Context, projectID uuid.UUID, regulation RegulationType) (*RegulatoryClassification, error) {
	var c RegulatoryClassification
	var reg, rl string
	var ragSources, requirements []byte

	err := s.pool.QueryRow(ctx, `
		SELECT
			id, project_id, regulation, classification_result,
			risk_level, confidence, reasoning,
			rag_sources, requirements,
			created_at, updated_at
		FROM iace_classifications
		WHERE project_id = $1 AND regulation = $2
	`, projectID, string(regulation)).Scan(
		&c.ID, &c.ProjectID, &reg, &c.ClassificationResult,
		&rl, &c.Confidence, &c.Reasoning,
		&ragSources, &requirements,
		&c.CreatedAt, &c.UpdatedAt,
	)
	if err == pgx.ErrNoRows {
		return nil, nil
	}
	if err != nil {
		return nil, fmt.Errorf("get classification: %w", err)
	}

	c.Regulation = RegulationType(reg)
	c.RiskLevel = RiskLevel(rl)
	json.Unmarshal(ragSources, &c.RAGSources)
	json.Unmarshal(requirements, &c.Requirements)

	return &c, nil
}

// GetClassifications retrieves all classifications for a project
func (s *Store) GetClassifications(ctx context.Context, projectID uuid.UUID) ([]RegulatoryClassification, error) {
	rows, err := s.pool.Query(ctx, `
		SELECT
			id, project_id, regulation, classification_result,
			risk_level, confidence, reasoning,
			rag_sources, requirements,
			created_at, updated_at
		FROM iace_classifications
		WHERE project_id = $1
		ORDER BY regulation ASC
	`, projectID)
	if err != nil {
		return nil, fmt.Errorf("get classifications: %w", err)
	}
	defer rows.Close()

	var classifications []RegulatoryClassification
	for rows.Next() {
		var c RegulatoryClassification
		var reg, rl string
		var ragSources, requirements []byte

		err := rows.Scan(
			&c.ID, &c.ProjectID, &reg, &c.ClassificationResult,
			&rl, &c.Confidence, &c.Reasoning,
			&ragSources, &requirements,
			&c.CreatedAt, &c.UpdatedAt,
		)
		if err != nil {
			return nil, fmt.Errorf("get classifications scan: %w", err)
		}

		c.Regulation = RegulationType(reg)
		c.RiskLevel = RiskLevel(rl)
		json.Unmarshal(ragSources, &c.RAGSources)
		json.Unmarshal(requirements, &c.Requirements)

		classifications = append(classifications, c)
	}

	return classifications, nil
}
