package council

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

	"github.com/google/uuid"
	"github.com/jackc/pgx/v5/pgxpool"
	"github.com/rs/zerolog/log"
	"go.opentelemetry.io/otel/attribute"
	
	"github.com/chorus-services/whoosh/internal/tracing"
)

// CouncilComposer manages the formation and orchestration of project kickoff councils
type CouncilComposer struct {
	db       *pgxpool.Pool
	ctx      context.Context
	cancel   context.CancelFunc
}

// NewCouncilComposer creates a new council composer service
func NewCouncilComposer(db *pgxpool.Pool) *CouncilComposer {
	ctx, cancel := context.WithCancel(context.Background())
	
	return &CouncilComposer{
		db:     db,
		ctx:    ctx,
		cancel: cancel,
	}
}

// Close shuts down the council composer
func (cc *CouncilComposer) Close() error {
	cc.cancel()
	return nil
}

// FormCouncil creates a council composition for a project kickoff
func (cc *CouncilComposer) FormCouncil(ctx context.Context, request *CouncilFormationRequest) (*CouncilComposition, error) {
	ctx, span := tracing.StartCouncilSpan(ctx, "form_council", "")
	defer span.End()
	
	startTime := time.Now()
	councilID := uuid.New()
	
	// Add tracing attributes
	span.SetAttributes(
		attribute.String("council.id", councilID.String()),
		attribute.String("project.name", request.ProjectName),
		attribute.String("repository.name", request.Repository),
		attribute.String("project.brief", request.ProjectBrief),
	)
	
	// Add goal.id and pulse.id if available in the request
	if request.GoalID != "" {
		span.SetAttributes(attribute.String("goal.id", request.GoalID))
	}
	if request.PulseID != "" {
		span.SetAttributes(attribute.String("pulse.id", request.PulseID))
	}
	
	log.Info().
		Str("council_id", councilID.String()).
		Str("project_name", request.ProjectName).
		Str("repository", request.Repository).
		Msg("🎭 Forming project kickoff council")
	
	// Create core council agents (always required)
	coreAgents := make([]CouncilAgent, len(CoreCouncilRoles))
	for i, roleName := range CoreCouncilRoles {
		agentID := fmt.Sprintf("council-%s-%s", strings.ReplaceAll(request.ProjectName, " ", "-"), roleName)
		coreAgents[i] = CouncilAgent{
			AgentID:    agentID,
			RoleName:   roleName,
			AgentName:  cc.formatRoleName(roleName),
			Required:   true,
			Deployed:   false,
			Status:     "pending",
		}
	}
	
	// Determine optional agents based on project characteristics
	optionalAgents := cc.selectOptionalAgents(request)
	
	// Create council composition
	composition := &CouncilComposition{
		CouncilID:      councilID,
		ProjectName:    request.ProjectName,
		CoreAgents:     coreAgents,
		OptionalAgents: optionalAgents,
		CreatedAt:      startTime,
		Status:         "forming",
	}
	
	// Store council composition in database
	err := cc.storeCouncilComposition(ctx, composition, request)
	if err != nil {
		tracing.SetSpanError(span, err)
		span.SetAttributes(attribute.String("council.formation.status", "failed"))
		return nil, fmt.Errorf("failed to store council composition: %w", err)
	}
	
	// Add success metrics to span
	span.SetAttributes(
		attribute.Int("council.core_agents.count", len(coreAgents)),
		attribute.Int("council.optional_agents.count", len(optionalAgents)),
		attribute.Int64("council.formation.duration_ms", time.Since(startTime).Milliseconds()),
		attribute.String("council.formation.status", "completed"),
	)
	
	log.Info().
		Str("council_id", councilID.String()).
		Int("core_agents", len(coreAgents)).
		Int("optional_agents", len(optionalAgents)).
		Dur("formation_time", time.Since(startTime)).
		Msg("✅ Council composition formed")
	
	return composition, nil
}

// selectOptionalAgents determines which optional council agents should be included
func (cc *CouncilComposer) selectOptionalAgents(request *CouncilFormationRequest) []CouncilAgent {
	var selectedAgents []CouncilAgent
	
	// Analyze project brief and characteristics to determine needed optional roles
	brief := strings.ToLower(request.ProjectBrief)
	
	// Data/AI projects
	if strings.Contains(brief, "ai") || strings.Contains(brief, "machine learning") || 
	   strings.Contains(brief, "data") || strings.Contains(brief, "analytics") {
		selectedAgents = append(selectedAgents, cc.createOptionalAgent("data-ai-architect", request.ProjectName))
	}
	
	// Privacy/compliance sensitive projects
	if strings.Contains(brief, "privacy") || strings.Contains(brief, "personal data") ||
	   strings.Contains(brief, "gdpr") || strings.Contains(brief, "compliance") {
		selectedAgents = append(selectedAgents, cc.createOptionalAgent("privacy-data-governance-officer", request.ProjectName))
	}
	
	// Regulated industries
	if strings.Contains(brief, "healthcare") || strings.Contains(brief, "finance") ||
	   strings.Contains(brief, "banking") || strings.Contains(brief, "regulated") {
		selectedAgents = append(selectedAgents, cc.createOptionalAgent("compliance-legal-liaison", request.ProjectName))
	}
	
	// Performance-critical systems
	if strings.Contains(brief, "performance") || strings.Contains(brief, "high-load") ||
	   strings.Contains(brief, "scale") || strings.Contains(brief, "benchmark") {
		selectedAgents = append(selectedAgents, cc.createOptionalAgent("performance-benchmarking-analyst", request.ProjectName))
	}
	
	// User-facing applications
	if strings.Contains(brief, "user interface") || strings.Contains(brief, "ui") ||
	   strings.Contains(brief, "ux") || strings.Contains(brief, "frontend") {
		selectedAgents = append(selectedAgents, cc.createOptionalAgent("ui-ux-designer", request.ProjectName))
	}
	
	// Mobile applications
	if strings.Contains(brief, "mobile") || strings.Contains(brief, "ios") ||
	   strings.Contains(brief, "android") || strings.Contains(brief, "app store") {
		selectedAgents = append(selectedAgents, cc.createOptionalAgent("ios-macos-developer", request.ProjectName))
	}
	
	// Games or graphics-intensive applications
	if strings.Contains(brief, "game") || strings.Contains(brief, "graphics") ||
	   strings.Contains(brief, "rendering") || strings.Contains(brief, "3d") {
		selectedAgents = append(selectedAgents, cc.createOptionalAgent("engine-programmer", request.ProjectName))
	}
	
	// Integration-heavy projects
	if strings.Contains(brief, "integration") || strings.Contains(brief, "api") ||
	   strings.Contains(brief, "microservice") || strings.Contains(brief, "third-party") {
		selectedAgents = append(selectedAgents, cc.createOptionalAgent("integration-architect", request.ProjectName))
	}
	
	// Cost-sensitive or enterprise projects
	if strings.Contains(brief, "budget") || strings.Contains(brief, "cost") ||
	   strings.Contains(brief, "enterprise") || strings.Contains(brief, "licensing") {
		selectedAgents = append(selectedAgents, cc.createOptionalAgent("cost-licensing-steward", request.ProjectName))
	}
	
	return selectedAgents
}

// createOptionalAgent creates an optional council agent
func (cc *CouncilComposer) createOptionalAgent(roleName, projectName string) CouncilAgent {
	agentID := fmt.Sprintf("council-%s-%s", strings.ReplaceAll(projectName, " ", "-"), roleName)
	return CouncilAgent{
		AgentID:   agentID,
		RoleName:  roleName,
		AgentName: cc.formatRoleName(roleName),
		Required:  false,
		Deployed:  false,
		Status:    "pending",
	}
}

// formatRoleName converts role key to human-readable name
func (cc *CouncilComposer) formatRoleName(roleName string) string {
	// Convert kebab-case to Title Case
	parts := strings.Split(roleName, "-")
	for i, part := range parts {
		parts[i] = strings.Title(part)
	}
	return strings.Join(parts, " ")
}

// storeCouncilComposition stores the council composition in the database
func (cc *CouncilComposer) storeCouncilComposition(ctx context.Context, composition *CouncilComposition, request *CouncilFormationRequest) error {
	// First, create a team record for this council (councils ARE teams)
	teamQuery := `
		INSERT INTO teams (id, name, description, status, created_at, updated_at)
		VALUES ($1, $2, $3, $4, $5, $6)
		ON CONFLICT (id) DO NOTHING
	`

	teamName := fmt.Sprintf("Council: %s", composition.ProjectName)
	teamDescription := fmt.Sprintf("Project kickoff council for %s", composition.ProjectName)

	_, err := cc.db.Exec(ctx, teamQuery,
		composition.CouncilID, // Use same ID for team and council
		teamName,
		teamDescription,
		"forming", // Same status as council
		composition.CreatedAt,
		composition.CreatedAt,
	)

	if err != nil {
		return fmt.Errorf("failed to create team record for council: %w", err)
	}

	// Store council metadata
	councilQuery := `
		INSERT INTO councils (id, project_name, repository, project_brief, status, created_at, task_id, issue_id, external_url, metadata)
		VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)
	`

	metadataJSON, _ := json.Marshal(request.Metadata)

	// Convert zero UUID to nil for task_id
	var taskID interface{}
	if request.TaskID == uuid.Nil {
		taskID = nil
	} else {
		taskID = request.TaskID
	}

	_, err = cc.db.Exec(ctx, councilQuery,
		composition.CouncilID,
		composition.ProjectName,
		request.Repository,
		request.ProjectBrief,
		composition.Status,
		composition.CreatedAt,
		taskID,
		request.IssueID,
		request.ExternalURL,
		metadataJSON,
	)

	if err != nil {
		return fmt.Errorf("failed to store council metadata: %w", err)
	}
	
	// Store council agents
	for _, agent := range composition.CoreAgents {
		err = cc.storeCouncilAgent(ctx, composition.CouncilID, agent)
		if err != nil {
			return fmt.Errorf("failed to store core agent %s: %w", agent.AgentID, err)
		}
	}
	
	for _, agent := range composition.OptionalAgents {
		err = cc.storeCouncilAgent(ctx, composition.CouncilID, agent)
		if err != nil {
			return fmt.Errorf("failed to store optional agent %s: %w", agent.AgentID, err)
		}
	}
	
	return nil
}

// storeCouncilAgent stores a single council agent in the database
func (cc *CouncilComposer) storeCouncilAgent(ctx context.Context, councilID uuid.UUID, agent CouncilAgent) error {
	query := `
		INSERT INTO council_agents (council_id, agent_id, role_name, agent_name, required, deployed, status, created_at)
		VALUES ($1, $2, $3, $4, $5, $6, $7, NOW())
	`
	
	_, err := cc.db.Exec(ctx, query,
		councilID,
		agent.AgentID,
		agent.RoleName,
		agent.AgentName,
		agent.Required,
		agent.Deployed,
		agent.Status,
	)
	
	return err
}

// GetCouncilComposition retrieves a council composition by ID
func (cc *CouncilComposer) GetCouncilComposition(ctx context.Context, councilID uuid.UUID) (*CouncilComposition, error) {
	// First, get the council metadata
	councilQuery := `
		SELECT id, project_name, status, created_at
		FROM councils 
		WHERE id = $1
	`
	
	var composition CouncilComposition
	var status string
	var createdAt time.Time
	
	err := cc.db.QueryRow(ctx, councilQuery, councilID).Scan(
		&composition.CouncilID,
		&composition.ProjectName,
		&status,
		&createdAt,
	)
	
	if err != nil {
		return nil, fmt.Errorf("failed to query council: %w", err)
	}
	
	composition.Status = status
	composition.CreatedAt = createdAt
	
	// Get all agents for this council
	agentQuery := `
		SELECT agent_id, role_name, agent_name, required, deployed, status, deployed_at,
		       persona_status, persona_loaded_at, endpoint_url, persona_ack_payload
		FROM council_agents
		WHERE council_id = $1
		ORDER BY required DESC, role_name ASC
	`

	rows, err := cc.db.Query(ctx, agentQuery, councilID)
	if err != nil {
		return nil, fmt.Errorf("failed to query council agents: %w", err)
	}
	defer rows.Close()

	// Separate core and optional agents
	var coreAgents []CouncilAgent
	var optionalAgents []CouncilAgent

	for rows.Next() {
		var agent CouncilAgent
		var deployedAt *time.Time
		var personaStatus *string
		var personaLoadedAt *time.Time
		var endpointURL *string
		var personaAckPayload []byte

		err := rows.Scan(
			&agent.AgentID,
			&agent.RoleName,
			&agent.AgentName,
			&agent.Required,
			&agent.Deployed,
			&agent.Status,
			&deployedAt,
			&personaStatus,
			&personaLoadedAt,
			&endpointURL,
			&personaAckPayload,
		)

		if err != nil {
			return nil, fmt.Errorf("failed to scan agent row: %w", err)
		}

		agent.DeployedAt = deployedAt
		agent.PersonaStatus = personaStatus
		agent.PersonaLoadedAt = personaLoadedAt
		agent.EndpointURL = endpointURL

		// Parse JSON payload if present
		if personaAckPayload != nil {
			var payload map[string]interface{}
			if err := json.Unmarshal(personaAckPayload, &payload); err == nil {
				agent.PersonaAckPayload = payload
			}
		}
		
		if agent.Required {
			coreAgents = append(coreAgents, agent)
		} else {
			optionalAgents = append(optionalAgents, agent)
		}
	}
	
	if err = rows.Err(); err != nil {
		return nil, fmt.Errorf("error iterating agent rows: %w", err)
	}
	
	composition.CoreAgents = coreAgents
	composition.OptionalAgents = optionalAgents
	
	log.Info().
		Str("council_id", councilID.String()).
		Str("project_name", composition.ProjectName).
		Int("core_agents", len(coreAgents)).
		Int("optional_agents", len(optionalAgents)).
		Msg("Retrieved council composition")
	
	return &composition, nil
}

// UpdateCouncilStatus updates the status of a council
func (cc *CouncilComposer) UpdateCouncilStatus(ctx context.Context, councilID uuid.UUID, status string) error {
	query := `UPDATE councils SET status = $1, updated_at = NOW() WHERE id = $2`
	_, err := cc.db.Exec(ctx, query, status, councilID)
	return err
}