package iace

import (
	"context"
	"fmt"
	"strings"

	"github.com/google/uuid"
)

// CustomerStandardSuggestion aggregates one reusable mitigation across prior
// projects of the same customer. The same mitigation name may appear in
// multiple prior projects; we collapse them into a single suggestion and
// count the prior occurrences so the expert sees a confidence signal.
type CustomerStandardSuggestion struct {
	Name              string `json:"name"`
	ReductionType     string `json:"reduction_type"`
	Description       string `json:"description"`
	// SourceProjectCount tells the expert in how many of the customer's
	// earlier projects this mitigation was already flagged. Higher count
	// = stronger reuse signal.
	SourceProjectCount   int      `json:"source_project_count"`
	SourceProjectNames   []string `json:"source_project_names"`
	IsCustomerStandard   bool     `json:"is_customer_standard"`
	HasVerifiedInstances bool     `json:"has_verified_instances"`
}

// ListCustomerStandardSuggestions returns reusable mitigations from prior
// projects of the same customer as projectID. The customer key is the
// case-insensitive trimmed customer_name; an empty customer_name short-
// circuits to an empty result.
//
// includeVerified=false → only mitigations with is_customer_standard=true
// includeVerified=true  → also include status='verified' mitigations
//                         (broader pool, useful when the customer-standard
//                         habit isn't yet established in the data)
func (s *Store) ListCustomerStandardSuggestions(
	ctx context.Context,
	projectID uuid.UUID,
	includeVerified bool,
) ([]CustomerStandardSuggestion, error) {
	// Resolve the customer + tenant for the current project.
	var tenantID uuid.UUID
	var customerName string
	err := s.pool.QueryRow(ctx,
		`SELECT tenant_id, COALESCE(customer_name, '') FROM iace_projects WHERE id = $1`,
		projectID,
	).Scan(&tenantID, &customerName)
	if err != nil {
		return nil, fmt.Errorf("resolve project for customer-standards: %w", err)
	}
	customerName = strings.TrimSpace(customerName)
	if customerName == "" {
		return []CustomerStandardSuggestion{}, nil
	}

	filterClause := "m.is_customer_standard = TRUE"
	if includeVerified {
		filterClause = "(m.is_customer_standard = TRUE OR m.status = 'verified')"
	}

	query := fmt.Sprintf(`
		SELECT
			m.name,
			m.reduction_type,
			MAX(m.description) AS description,
			COUNT(DISTINCT p.id) AS source_count,
			array_agg(DISTINCT p.machine_name ORDER BY p.machine_name) AS source_names,
			BOOL_OR(m.is_customer_standard) AS has_customer_std,
			BOOL_OR(m.status = 'verified')   AS has_verified
		FROM iace_mitigations m
		JOIN iace_hazards   h ON h.id = m.hazard_id
		JOIN iace_projects  p ON p.id = h.project_id
		WHERE p.tenant_id = $1
		  AND p.id <> $2
		  AND p.archived_at IS NULL
		  AND LOWER(TRIM(COALESCE(p.customer_name, ''))) = LOWER($3)
		  AND %s
		GROUP BY m.name, m.reduction_type
		ORDER BY source_count DESC, m.name
	`, filterClause)

	rows, err := s.pool.Query(ctx, query, tenantID, projectID, customerName)
	if err != nil {
		return nil, fmt.Errorf("query customer-standards: %w", err)
	}
	defer rows.Close()

	var out []CustomerStandardSuggestion
	for rows.Next() {
		var sg CustomerStandardSuggestion
		if scanErr := rows.Scan(
			&sg.Name, &sg.ReductionType, &sg.Description,
			&sg.SourceProjectCount, &sg.SourceProjectNames,
			&sg.IsCustomerStandard, &sg.HasVerifiedInstances,
		); scanErr != nil {
			return nil, fmt.Errorf("scan customer-standards: %w", scanErr)
		}
		out = append(out, sg)
	}
	return out, nil
}

// ImportCustomerStandardSuggestion applies a suggestion to the current
// project: for every hazard in the project whose name matches one of the
// suggestion's source hazards (by mitigation.name → hazard.name pairing in
// prior projects), it ensures a relevant + customer-standard mitigation
// exists. New mitigations are inserted via CreateMitigation (idempotent
// via UNIQUE(hazard_id, name)), existing ones are flipped to
// is_relevant=true + is_customer_standard=true + status='verified'.
//
// Returns the number of mitigations affected (created + updated).
func (s *Store) ImportCustomerStandardSuggestion(
	ctx context.Context,
	projectID uuid.UUID,
	mitigationName string,
) (int, error) {
	// Find tenant + customer of the target project.
	var tenantID uuid.UUID
	var customerName string
	if err := s.pool.QueryRow(ctx,
		`SELECT tenant_id, COALESCE(customer_name, '') FROM iace_projects WHERE id = $1`,
		projectID,
	).Scan(&tenantID, &customerName); err != nil {
		return 0, fmt.Errorf("resolve project: %w", err)
	}
	customerName = strings.TrimSpace(customerName)
	if customerName == "" {
		return 0, fmt.Errorf("project has no customer_name — nothing to reuse")
	}

	// Collect the hazard names this mitigation was attached to in the
	// customer's prior projects + a representative reduction_type/description.
	priorRows, err := s.pool.Query(ctx, `
		SELECT DISTINCT h.name, m.reduction_type, COALESCE(m.description, '')
		FROM iace_mitigations m
		JOIN iace_hazards  h ON h.id = m.hazard_id
		JOIN iace_projects p ON p.id = h.project_id
		WHERE p.tenant_id = $1
		  AND p.id <> $2
		  AND p.archived_at IS NULL
		  AND LOWER(TRIM(COALESCE(p.customer_name, ''))) = LOWER($3)
		  AND m.name = $4
	`, tenantID, projectID, customerName, mitigationName)
	if err != nil {
		return 0, fmt.Errorf("collect prior hazards: %w", err)
	}
	defer priorRows.Close()

	type proto struct{ hazardName, reductionType, description string }
	var prototypes []proto
	for priorRows.Next() {
		var p proto
		if err := priorRows.Scan(&p.hazardName, &p.reductionType, &p.description); err != nil {
			return 0, err
		}
		prototypes = append(prototypes, p)
	}
	if len(prototypes) == 0 {
		return 0, nil
	}

	// For every prototype hazard name, find the matching hazard in the
	// current project (same name) and ensure a relevant + customer-standard
	// mitigation with mitigationName exists for it.
	affected := 0
	for _, p := range prototypes {
		var hazardIDs []uuid.UUID
		hazRows, err := s.pool.Query(ctx,
			`SELECT id FROM iace_hazards WHERE project_id = $1 AND name = $2`,
			projectID, p.hazardName,
		)
		if err != nil {
			return affected, fmt.Errorf("find target hazards: %w", err)
		}
		for hazRows.Next() {
			var hid uuid.UUID
			if scanErr := hazRows.Scan(&hid); scanErr != nil {
				hazRows.Close()
				return affected, scanErr
			}
			hazardIDs = append(hazardIDs, hid)
		}
		hazRows.Close()

		for _, hid := range hazardIDs {
			// Idempotent insert; UPDATE sets relevance + verified state.
			_, err := s.CreateMitigation(ctx, CreateMitigationRequest{
				HazardID:      hid,
				Name:          mitigationName,
				Description:   p.description,
				ReductionType: ReductionType(p.reductionType),
			})
			if err != nil {
				return affected, fmt.Errorf("create mitigation: %w", err)
			}
			if _, err := s.pool.Exec(ctx, `
				UPDATE iace_mitigations
				   SET is_relevant = TRUE,
				       is_customer_standard = TRUE,
				       status = 'verified',
				       updated_at = NOW()
				 WHERE hazard_id = $1 AND name = $2
			`, hid, mitigationName); err != nil {
				return affected, fmt.Errorf("upgrade mitigation: %w", err)
			}
			affected++
		}
	}
	return affected, nil
}
