package iace

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

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

// ============================================================================
// Tech File Section Operations
// ============================================================================

// CreateTechFileSection creates a new section in the technical file.
// tenantID is extracted from the project's owning tenant.
func (s *Store) CreateTechFileSection(ctx context.Context, projectID uuid.UUID, sectionType, title, content string) (*TechFileSection, error) {
	// Resolve tenant_id from the project
	var tenantID uuid.UUID
	err := s.pool.QueryRow(ctx,
		`SELECT tenant_id FROM iace_projects WHERE id = $1`, projectID,
	).Scan(&tenantID)
	if err != nil {
		return nil, fmt.Errorf("resolve tenant_id for project %s: %w", projectID, err)
	}

	tf := &TechFileSection{
		ID:          uuid.New(),
		ProjectID:   projectID,
		SectionType: sectionType,
		Title:       title,
		Content:     content,
		Version:     1,
		Status:      TechFileSectionStatusDraft,
		CreatedAt:   time.Now().UTC(),
		UpdatedAt:   time.Now().UTC(),
	}

	_, err = s.pool.Exec(ctx, `
		INSERT INTO iace_tech_file_sections (
			id, project_id, tenant_id, section_type, title, html_content,
			status, approved_by, approved_at, metadata,
			created_at, updated_at
		) VALUES (
			$1, $2, $3, $4, $5, $6,
			$7, $8, $9, $10,
			$11, $12
		)
	`,
		tf.ID, tf.ProjectID, tenantID, tf.SectionType, tf.Title, tf.Content,
		string(tf.Status), "", nil, nil,
		tf.CreatedAt, tf.UpdatedAt,
	)
	if err != nil {
		return nil, fmt.Errorf("create tech file section: %w", err)
	}

	return tf, nil
}

// UpdateTechFileSection updates the content of a tech file section
func (s *Store) UpdateTechFileSection(ctx context.Context, id uuid.UUID, content string) error {
	_, err := s.pool.Exec(ctx, `
		UPDATE iace_tech_file_sections SET
			html_content = $2,
			status = $3,
			updated_at = NOW()
		WHERE id = $1
	`, id, content, string(TechFileSectionStatusDraft))
	if err != nil {
		return fmt.Errorf("update tech file section: %w", err)
	}
	return nil
}

// ApproveTechFileSection marks a tech file section as approved
func (s *Store) ApproveTechFileSection(ctx context.Context, id uuid.UUID, approvedBy string) error {
	now := time.Now().UTC()

	_, err := s.pool.Exec(ctx, `
		UPDATE iace_tech_file_sections SET
			status = $2,
			approved_by = $3,
			approved_at = $4,
			updated_at = $4
		WHERE id = $1
	`, id, string(TechFileSectionStatusApproved), approvedBy, now)
	if err != nil {
		return fmt.Errorf("approve tech file section: %w", err)
	}

	return nil
}

// ListTechFileSections lists all tech file sections for a project
func (s *Store) ListTechFileSections(ctx context.Context, projectID uuid.UUID) ([]TechFileSection, error) {
	rows, err := s.pool.Query(ctx, `
		SELECT
			id, project_id, section_type, title,
			COALESCE(html_content, '') AS content, status,
			COALESCE(approved_by, '') AS approved_by, approved_at,
			COALESCE(metadata, '{}'::jsonb) AS metadata,
			created_at, COALESCE(updated_at, created_at) AS updated_at
		FROM iace_tech_file_sections WHERE project_id = $1
		ORDER BY created_at ASC
	`, projectID)
	if err != nil {
		return nil, fmt.Errorf("list tech file sections: %w", err)
	}
	defer rows.Close()

	var sections []TechFileSection
	for rows.Next() {
		var tf TechFileSection
		var status string
		var approvedByStr string
		var metadata []byte

		err := rows.Scan(
			&tf.ID, &tf.ProjectID, &tf.SectionType, &tf.Title,
			&tf.Content, &status,
			&approvedByStr, &tf.ApprovedAt, &metadata,
			&tf.CreatedAt, &tf.UpdatedAt,
		)
		if err != nil {
			return nil, fmt.Errorf("list tech file sections scan: %w", err)
		}

		tf.Status = TechFileSectionStatus(status)
		tf.Version = 1
		if approvedByStr != "" {
			if parsed, e := uuid.Parse(approvedByStr); e == nil {
				tf.ApprovedBy = parsed
			}
		}
		json.Unmarshal(metadata, &tf.Metadata)

		sections = append(sections, tf)
	}

	return sections, nil
}

// ============================================================================
// Monitoring Event Operations
// ============================================================================

// CreateMonitoringEvent creates a new post-market monitoring event
func (s *Store) CreateMonitoringEvent(ctx context.Context, req CreateMonitoringEventRequest) (*MonitoringEvent, error) {
	me := &MonitoringEvent{
		ID:        uuid.New(),
		ProjectID: req.ProjectID,
		EventType: req.EventType,
		Title:     req.Title,
		Description: req.Description,
		Severity:  req.Severity,
		Status:    "open",
		CreatedAt: time.Now().UTC(),
		UpdatedAt: time.Now().UTC(),
	}

	_, err := s.pool.Exec(ctx, `
		INSERT INTO iace_monitoring_events (
			id, project_id, event_type, title, description,
			severity, impact_assessment, status,
			resolved_at, resolved_by, metadata,
			created_at, updated_at
		) VALUES (
			$1, $2, $3, $4, $5,
			$6, $7, $8,
			$9, $10, $11,
			$12, $13
		)
	`,
		me.ID, me.ProjectID, string(me.EventType), me.Title, me.Description,
		me.Severity, "", me.Status,
		nil, uuid.Nil, nil,
		me.CreatedAt, me.UpdatedAt,
	)
	if err != nil {
		return nil, fmt.Errorf("create monitoring event: %w", err)
	}

	return me, nil
}

// ListMonitoringEvents lists all monitoring events for a project
func (s *Store) ListMonitoringEvents(ctx context.Context, projectID uuid.UUID) ([]MonitoringEvent, error) {
	rows, err := s.pool.Query(ctx, `
		SELECT
			id, project_id, event_type, title, description,
			severity, impact_assessment, status,
			resolved_at, resolved_by, metadata,
			created_at, updated_at
		FROM iace_monitoring_events WHERE project_id = $1
		ORDER BY created_at DESC
	`, projectID)
	if err != nil {
		return nil, fmt.Errorf("list monitoring events: %w", err)
	}
	defer rows.Close()

	var events []MonitoringEvent
	for rows.Next() {
		var me MonitoringEvent
		var eventType string
		var metadata []byte

		err := rows.Scan(
			&me.ID, &me.ProjectID, &eventType, &me.Title, &me.Description,
			&me.Severity, &me.ImpactAssessment, &me.Status,
			&me.ResolvedAt, &me.ResolvedBy, &metadata,
			&me.CreatedAt, &me.UpdatedAt,
		)
		if err != nil {
			return nil, fmt.Errorf("list monitoring events scan: %w", err)
		}

		me.EventType = MonitoringEventType(eventType)
		json.Unmarshal(metadata, &me.Metadata)

		events = append(events, me)
	}

	return events, nil
}

// UpdateMonitoringEvent updates a monitoring event with a dynamic set of fields
func (s *Store) UpdateMonitoringEvent(ctx context.Context, id uuid.UUID, updates map[string]interface{}) (*MonitoringEvent, error) {
	if len(updates) == 0 {
		return s.getMonitoringEvent(ctx, id)
	}

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

	for key, val := range updates {
		switch key {
		case "title", "description", "severity", "impact_assessment", "status":
			query += fmt.Sprintf(", %s = $%d", key, argIdx)
			args = append(args, val)
			argIdx++
		case "event_type":
			query += fmt.Sprintf(", event_type = $%d", argIdx)
			args = append(args, val)
			argIdx++
		case "resolved_at":
			query += fmt.Sprintf(", resolved_at = $%d", argIdx)
			args = append(args, val)
			argIdx++
		case "resolved_by":
			query += fmt.Sprintf(", resolved_by = $%d", argIdx)
			args = append(args, val)
			argIdx++
		case "metadata":
			metaJSON, _ := json.Marshal(val)
			query += fmt.Sprintf(", metadata = $%d", argIdx)
			args = append(args, metaJSON)
			argIdx++
		}
	}

	query += " WHERE id = $1"

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

	return s.getMonitoringEvent(ctx, id)
}

// getMonitoringEvent is a helper to fetch a single monitoring event by ID
func (s *Store) getMonitoringEvent(ctx context.Context, id uuid.UUID) (*MonitoringEvent, error) {
	var me MonitoringEvent
	var eventType string
	var metadata []byte

	err := s.pool.QueryRow(ctx, `
		SELECT
			id, project_id, event_type, title, description,
			severity, impact_assessment, status,
			resolved_at, resolved_by, metadata,
			created_at, updated_at
		FROM iace_monitoring_events WHERE id = $1
	`, id).Scan(
		&me.ID, &me.ProjectID, &eventType, &me.Title, &me.Description,
		&me.Severity, &me.ImpactAssessment, &me.Status,
		&me.ResolvedAt, &me.ResolvedBy, &metadata,
		&me.CreatedAt, &me.UpdatedAt,
	)
	if err == pgx.ErrNoRows {
		return nil, nil
	}
	if err != nil {
		return nil, fmt.Errorf("get monitoring event: %w", err)
	}

	me.EventType = MonitoringEventType(eventType)
	json.Unmarshal(metadata, &me.Metadata)

	return &me, nil
}

// ============================================================================
// Audit Trail Operations
// ============================================================================

// AddAuditEntry adds an immutable audit trail entry
func (s *Store) AddAuditEntry(ctx context.Context, projectID uuid.UUID, entityType string, entityID uuid.UUID, action AuditAction, userID string, oldValues, newValues json.RawMessage) error {
	id := uuid.New()
	now := time.Now().UTC()

	userUUID, err := uuid.Parse(userID)
	if err != nil {
		return fmt.Errorf("invalid user_id UUID: %w", err)
	}

	// Compute a simple hash for integrity: sha256(entityType + entityID + action + timestamp)
	hashInput := fmt.Sprintf("%s:%s:%s:%s:%s", projectID, entityType, entityID, string(action), now.Format(time.RFC3339Nano))
	// Use a simple deterministic hash representation
	hash := fmt.Sprintf("%x", hashInput)

	_, err = s.pool.Exec(ctx, `
		INSERT INTO iace_audit_trail (
			id, project_id, entity_type, entity_id,
			action, user_id, old_values, new_values,
			hash, created_at
		) VALUES (
			$1, $2, $3, $4,
			$5, $6, $7, $8,
			$9, $10
		)
	`,
		id, projectID, entityType, entityID,
		string(action), userUUID, oldValues, newValues,
		hash, now,
	)
	if err != nil {
		return fmt.Errorf("add audit entry: %w", err)
	}

	return nil
}

// ListAuditTrail lists all audit trail entries for a project, newest first
func (s *Store) ListAuditTrail(ctx context.Context, projectID uuid.UUID) ([]AuditTrailEntry, error) {
	rows, err := s.pool.Query(ctx, `
		SELECT
			id, project_id, entity_type, entity_id,
			action, user_id, old_values, new_values,
			hash, created_at
		FROM iace_audit_trail WHERE project_id = $1
		ORDER BY created_at DESC
	`, projectID)
	if err != nil {
		return nil, fmt.Errorf("list audit trail: %w", err)
	}
	defer rows.Close()

	var entries []AuditTrailEntry
	for rows.Next() {
		var e AuditTrailEntry
		var action string

		err := rows.Scan(
			&e.ID, &e.ProjectID, &e.EntityType, &e.EntityID,
			&action, &e.UserID, &e.OldValues, &e.NewValues,
			&e.Hash, &e.CreatedAt,
		)
		if err != nil {
			return nil, fmt.Errorf("list audit trail scan: %w", err)
		}

		e.Action = AuditAction(action)
		entries = append(entries, e)
	}

	return entries, nil
}

// HasAuditEntryForType checks if an audit trail entry exists for the given entity type within a project.
func (s *Store) HasAuditEntryForType(ctx context.Context, projectID uuid.UUID, entityType string) (bool, error) {
	var exists bool
	err := s.pool.QueryRow(ctx, `
		SELECT EXISTS(
			SELECT 1 FROM iace_audit_trail
			WHERE project_id = $1 AND entity_type = $2
		)
	`, projectID, entityType).Scan(&exists)
	if err != nil {
		return false, fmt.Errorf("has audit entry: %w", err)
	}
	return exists, nil
}
