package composer

import (
	"bytes"
	"context"
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"time"

	"github.com/google/uuid"
	"github.com/rs/zerolog/log"
)

// SpecKitClient handles communication with the spec-kit service
type SpecKitClient struct {
	baseURL    string
	httpClient *http.Client
	config     *SpecKitClientConfig
}

// SpecKitClientConfig contains configuration for the spec-kit client
type SpecKitClientConfig struct {
	ServiceURL         string        `json:"service_url"`
	Timeout            time.Duration `json:"timeout"`
	MaxRetries         int           `json:"max_retries"`
	RetryDelay         time.Duration `json:"retry_delay"`
	EnableCircuitBreaker bool        `json:"enable_circuit_breaker"`
	UserAgent          string        `json:"user_agent"`
}

// ProjectInitializeRequest for creating new spec-kit projects
type ProjectInitializeRequest struct {
	ProjectName    string                 `json:"project_name"`
	Description    string                 `json:"description"`
	RepositoryURL  string                 `json:"repository_url,omitempty"`
	ChorusMetadata map[string]interface{} `json:"chorus_metadata"`
}

// ProjectInitializeResponse from spec-kit service initialization
type ProjectInitializeResponse struct {
	ProjectID      string `json:"project_id"`
	BranchName     string `json:"branch_name"`
	SpecFilePath   string `json:"spec_file_path"`
	FeatureNumber  string `json:"feature_number"`
	Status         string `json:"status"`
}

// ConstitutionRequest for executing constitution phase
type ConstitutionRequest struct {
	PrinciplesDescription string                 `json:"principles_description"`
	OrganizationContext   map[string]interface{} `json:"organization_context"`
}

// ConstitutionResponse from constitution phase execution
type ConstitutionResponse struct {
	Constitution ConstitutionData `json:"constitution"`
	FilePath     string           `json:"file_path"`
	Status       string           `json:"status"`
}

// ConstitutionData contains the structured constitution information
type ConstitutionData struct {
	Principles    []Principle `json:"principles"`
	Governance    string      `json:"governance"`
	Version       string      `json:"version"`
	RatifiedDate  string      `json:"ratified_date"`
}

// Principle represents a single principle in the constitution
type Principle struct {
	Name        string `json:"name"`
	Description string `json:"description"`
}

// SpecificationRequest for executing specification phase
type SpecificationRequest struct {
	FeatureDescription  string   `json:"feature_description"`
	AcceptanceCriteria  []string `json:"acceptance_criteria"`
}

// SpecificationResponse from specification phase execution
type SpecificationResponse struct {
	Specification       SpecificationData `json:"specification"`
	FilePath            string            `json:"file_path"`
	CompletenessScore   float64           `json:"completeness_score"`
	ClarificationsNeeded []string         `json:"clarifications_needed"`
	Status              string            `json:"status"`
}

// SpecificationData contains structured specification information
type SpecificationData struct {
	FeatureName           string              `json:"feature_name"`
	UserScenarios         []UserScenario      `json:"user_scenarios"`
	FunctionalRequirements []Requirement      `json:"functional_requirements"`
	Entities              []Entity            `json:"entities"`
}

// UserScenario represents a user story or scenario
type UserScenario struct {
	PrimaryStory         string   `json:"primary_story"`
	AcceptanceScenarios  []string `json:"acceptance_scenarios"`
}

// Requirement represents a functional requirement
type Requirement struct {
	ID          string `json:"id"`
	Requirement string `json:"requirement"`
}

// Entity represents a key business entity
type Entity struct {
	Name        string `json:"name"`
	Description string `json:"description"`
}

// PlanningRequest for executing planning phase
type PlanningRequest struct {
	TechStack               map[string]interface{} `json:"tech_stack"`
	ArchitecturePreferences map[string]interface{} `json:"architecture_preferences"`
}

// PlanningResponse from planning phase execution
type PlanningResponse struct {
	Plan       PlanData `json:"plan"`
	FilePath   string   `json:"file_path"`
	Status     string   `json:"status"`
}

// PlanData contains structured planning information
type PlanData struct {
	TechStack       map[string]interface{} `json:"tech_stack"`
	Architecture    map[string]interface{} `json:"architecture"`
	Implementation  map[string]interface{} `json:"implementation"`
	TestingStrategy map[string]interface{} `json:"testing_strategy"`
}

// TasksResponse from tasks phase execution
type TasksResponse struct {
	Tasks    TasksData `json:"tasks"`
	FilePath string    `json:"file_path"`
	Status   string    `json:"status"`
}

// TasksData contains structured task information
type TasksData struct {
	SetupTasks       []Task `json:"setup_tasks"`
	CoreTasks        []Task `json:"core_tasks"`
	IntegrationTasks []Task `json:"integration_tasks"`
	PolishTasks      []Task `json:"polish_tasks"`
}

// Task represents a single implementation task
type Task struct {
	ID           string   `json:"id"`
	Title        string   `json:"title"`
	Description  string   `json:"description"`
	Dependencies []string `json:"dependencies"`
	Parallel     bool     `json:"parallel"`
	EstimatedHours int    `json:"estimated_hours"`
}

// ProjectStatusResponse contains current project status
type ProjectStatusResponse struct {
	ProjectID        string                 `json:"project_id"`
	CurrentPhase     string                 `json:"current_phase"`
	PhasesCompleted  []string               `json:"phases_completed"`
	OverallProgress  float64                `json:"overall_progress"`
	Artifacts        []ArtifactInfo         `json:"artifacts"`
	QualityMetrics   map[string]float64     `json:"quality_metrics"`
}

// ArtifactInfo contains information about generated artifacts
type ArtifactInfo struct {
	Type         string    `json:"type"`
	Path         string    `json:"path"`
	LastModified time.Time `json:"last_modified"`
}

// NewSpecKitClient creates a new spec-kit service client
func NewSpecKitClient(config *SpecKitClientConfig) *SpecKitClient {
	if config == nil {
		config = &SpecKitClientConfig{
			Timeout:    30 * time.Second,
			MaxRetries: 3,
			RetryDelay: 1 * time.Second,
			UserAgent:  "WHOOSH-SpecKit-Client/1.0",
		}
	}

	return &SpecKitClient{
		baseURL: config.ServiceURL,
		httpClient: &http.Client{
			Timeout: config.Timeout,
		},
		config: config,
	}
}

// InitializeProject creates a new spec-kit project
func (c *SpecKitClient) InitializeProject(
	ctx context.Context,
	req *ProjectInitializeRequest,
) (*ProjectInitializeResponse, error) {
	log.Info().
		Str("project_name", req.ProjectName).
		Str("council_id", fmt.Sprintf("%v", req.ChorusMetadata["council_id"])).
		Msg("Initializing spec-kit project")

	var response ProjectInitializeResponse
	err := c.makeRequest(ctx, "POST", "/v1/projects/initialize", req, &response)
	if err != nil {
		return nil, fmt.Errorf("failed to initialize project: %w", err)
	}

	log.Info().
		Str("project_id", response.ProjectID).
		Str("branch_name", response.BranchName).
		Str("status", response.Status).
		Msg("Spec-kit project initialized successfully")

	return &response, nil
}

// ExecuteConstitution runs the constitution phase
func (c *SpecKitClient) ExecuteConstitution(
	ctx context.Context,
	projectID string,
	req *ConstitutionRequest,
) (*ConstitutionResponse, error) {
	log.Info().
		Str("project_id", projectID).
		Msg("Executing constitution phase")

	var response ConstitutionResponse
	url := fmt.Sprintf("/v1/projects/%s/constitution", projectID)
	err := c.makeRequest(ctx, "POST", url, req, &response)
	if err != nil {
		return nil, fmt.Errorf("failed to execute constitution phase: %w", err)
	}

	log.Info().
		Str("project_id", projectID).
		Int("principles_count", len(response.Constitution.Principles)).
		Str("status", response.Status).
		Msg("Constitution phase completed")

	return &response, nil
}

// ExecuteSpecification runs the specification phase
func (c *SpecKitClient) ExecuteSpecification(
	ctx context.Context,
	projectID string,
	req *SpecificationRequest,
) (*SpecificationResponse, error) {
	log.Info().
		Str("project_id", projectID).
		Msg("Executing specification phase")

	var response SpecificationResponse
	url := fmt.Sprintf("/v1/projects/%s/specify", projectID)
	err := c.makeRequest(ctx, "POST", url, req, &response)
	if err != nil {
		return nil, fmt.Errorf("failed to execute specification phase: %w", err)
	}

	log.Info().
		Str("project_id", projectID).
		Str("feature_name", response.Specification.FeatureName).
		Float64("completeness_score", response.CompletenessScore).
		Int("clarifications_needed", len(response.ClarificationsNeeded)).
		Str("status", response.Status).
		Msg("Specification phase completed")

	return &response, nil
}

// ExecutePlanning runs the planning phase
func (c *SpecKitClient) ExecutePlanning(
	ctx context.Context,
	projectID string,
	req *PlanningRequest,
) (*PlanningResponse, error) {
	log.Info().
		Str("project_id", projectID).
		Msg("Executing planning phase")

	var response PlanningResponse
	url := fmt.Sprintf("/v1/projects/%s/plan", projectID)
	err := c.makeRequest(ctx, "POST", url, req, &response)
	if err != nil {
		return nil, fmt.Errorf("failed to execute planning phase: %w", err)
	}

	log.Info().
		Str("project_id", projectID).
		Str("status", response.Status).
		Msg("Planning phase completed")

	return &response, nil
}

// ExecuteTasks runs the tasks phase
func (c *SpecKitClient) ExecuteTasks(
	ctx context.Context,
	projectID string,
) (*TasksResponse, error) {
	log.Info().
		Str("project_id", projectID).
		Msg("Executing tasks phase")

	var response TasksResponse
	url := fmt.Sprintf("/v1/projects/%s/tasks", projectID)
	err := c.makeRequest(ctx, "POST", url, nil, &response)
	if err != nil {
		return nil, fmt.Errorf("failed to execute tasks phase: %w", err)
	}

	totalTasks := len(response.Tasks.SetupTasks) +
		len(response.Tasks.CoreTasks) +
		len(response.Tasks.IntegrationTasks) +
		len(response.Tasks.PolishTasks)

	log.Info().
		Str("project_id", projectID).
		Int("total_tasks", totalTasks).
		Str("status", response.Status).
		Msg("Tasks phase completed")

	return &response, nil
}

// GetProjectStatus retrieves current project status
func (c *SpecKitClient) GetProjectStatus(
	ctx context.Context,
	projectID string,
) (*ProjectStatusResponse, error) {
	log.Debug().
		Str("project_id", projectID).
		Msg("Retrieving project status")

	var response ProjectStatusResponse
	url := fmt.Sprintf("/v1/projects/%s/status", projectID)
	err := c.makeRequest(ctx, "GET", url, nil, &response)
	if err != nil {
		return nil, fmt.Errorf("failed to get project status: %w", err)
	}

	return &response, nil
}

// ExecuteWorkflow executes a complete spec-kit workflow
func (c *SpecKitClient) ExecuteWorkflow(
	ctx context.Context,
	req *SpecKitWorkflowRequest,
) (*SpecKitWorkflowResponse, error) {
	startTime := time.Now()

	log.Info().
		Str("project_name", req.ProjectName).
		Strs("phases", req.WorkflowPhases).
		Msg("Starting complete spec-kit workflow execution")

	// Step 1: Initialize project
	initReq := &ProjectInitializeRequest{
		ProjectName:    req.ProjectName,
		Description:    req.Description,
		RepositoryURL:  req.RepositoryURL,
		ChorusMetadata: req.ChorusMetadata,
	}

	initResp, err := c.InitializeProject(ctx, initReq)
	if err != nil {
		return nil, fmt.Errorf("workflow initialization failed: %w", err)
	}

	projectID := initResp.ProjectID
	var artifacts []SpecKitArtifact
	phasesCompleted := []string{}

	// Execute each requested phase
	for _, phase := range req.WorkflowPhases {
		switch phase {
		case "constitution":
			constReq := &ConstitutionRequest{
				PrinciplesDescription: "Create project principles focused on quality, testing, and performance",
				OrganizationContext: req.ChorusMetadata,
			}
			constResp, err := c.ExecuteConstitution(ctx, projectID, constReq)
			if err != nil {
				log.Error().Err(err).Str("phase", phase).Msg("Phase execution failed")
				continue
			}

			artifact := SpecKitArtifact{
				Type:      "constitution",
				Phase:     phase,
				Content:   map[string]interface{}{"constitution": constResp.Constitution},
				FilePath:  constResp.FilePath,
				CreatedAt: time.Now(),
				Quality:   0.95, // High quality for structured constitution
			}
			artifacts = append(artifacts, artifact)
			phasesCompleted = append(phasesCompleted, phase)

		case "specify":
			specReq := &SpecificationRequest{
				FeatureDescription: req.Description,
				AcceptanceCriteria: []string{}, // Could be extracted from description
			}
			specResp, err := c.ExecuteSpecification(ctx, projectID, specReq)
			if err != nil {
				log.Error().Err(err).Str("phase", phase).Msg("Phase execution failed")
				continue
			}

			artifact := SpecKitArtifact{
				Type:      "specification",
				Phase:     phase,
				Content:   map[string]interface{}{"specification": specResp.Specification},
				FilePath:  specResp.FilePath,
				CreatedAt: time.Now(),
				Quality:   specResp.CompletenessScore,
			}
			artifacts = append(artifacts, artifact)
			phasesCompleted = append(phasesCompleted, phase)

		case "plan":
			planReq := &PlanningRequest{
				TechStack: map[string]interface{}{
					"backend":  "Go with chi framework",
					"frontend": "React with TypeScript",
					"database": "PostgreSQL",
				},
				ArchitecturePreferences: map[string]interface{}{
					"pattern":   "microservices",
					"api_style": "REST",
					"testing":   "TDD",
				},
			}
			planResp, err := c.ExecutePlanning(ctx, projectID, planReq)
			if err != nil {
				log.Error().Err(err).Str("phase", phase).Msg("Phase execution failed")
				continue
			}

			artifact := SpecKitArtifact{
				Type:      "plan",
				Phase:     phase,
				Content:   map[string]interface{}{"plan": planResp.Plan},
				FilePath:  planResp.FilePath,
				CreatedAt: time.Now(),
				Quality:   0.90, // High quality for structured plan
			}
			artifacts = append(artifacts, artifact)
			phasesCompleted = append(phasesCompleted, phase)

		case "tasks":
			tasksResp, err := c.ExecuteTasks(ctx, projectID)
			if err != nil {
				log.Error().Err(err).Str("phase", phase).Msg("Phase execution failed")
				continue
			}

			artifact := SpecKitArtifact{
				Type:      "tasks",
				Phase:     phase,
				Content:   map[string]interface{}{"tasks": tasksResp.Tasks},
				FilePath:  tasksResp.FilePath,
				CreatedAt: time.Now(),
				Quality:   0.88, // Good quality for actionable tasks
			}
			artifacts = append(artifacts, artifact)
			phasesCompleted = append(phasesCompleted, phase)
		}
	}

	// Calculate quality metrics
	qualityMetrics := c.calculateQualityMetrics(artifacts)

	response := &SpecKitWorkflowResponse{
		ProjectID:       projectID,
		Status:          "completed",
		PhasesCompleted: phasesCompleted,
		Artifacts:       artifacts,
		QualityMetrics:  qualityMetrics,
		ProcessingTime:  time.Since(startTime),
		Metadata:        req.ChorusMetadata,
	}

	log.Info().
		Str("project_id", projectID).
		Int("phases_completed", len(phasesCompleted)).
		Int("artifacts_generated", len(artifacts)).
		Int64("total_time_ms", response.ProcessingTime.Milliseconds()).
		Msg("Complete spec-kit workflow execution finished")

	return response, nil
}

// GetTemplate retrieves workflow templates
func (c *SpecKitClient) GetTemplate(ctx context.Context, templateType string) (map[string]interface{}, error) {
	var template map[string]interface{}
	url := fmt.Sprintf("/v1/templates/%s", templateType)
	err := c.makeRequest(ctx, "GET", url, nil, &template)
	if err != nil {
		return nil, fmt.Errorf("failed to get template: %w", err)
	}
	return template, nil
}

// GetAnalytics retrieves analytics data
func (c *SpecKitClient) GetAnalytics(
	ctx context.Context,
	deploymentID uuid.UUID,
	timeRange string,
) (map[string]interface{}, error) {
	var analytics map[string]interface{}
	url := fmt.Sprintf("/v1/analytics?deployment_id=%s&time_range=%s", deploymentID.String(), timeRange)
	err := c.makeRequest(ctx, "GET", url, nil, &analytics)
	if err != nil {
		return nil, fmt.Errorf("failed to get analytics: %w", err)
	}
	return analytics, nil
}

// makeRequest handles HTTP requests with retries and error handling
func (c *SpecKitClient) makeRequest(
	ctx context.Context,
	method, endpoint string,
	requestBody interface{},
	responseBody interface{},
) error {
	url := c.baseURL + endpoint

	var bodyReader io.Reader
	if requestBody != nil {
		jsonBody, err := json.Marshal(requestBody)
		if err != nil {
			return fmt.Errorf("failed to marshal request body: %w", err)
		}
		bodyReader = bytes.NewBuffer(jsonBody)
	}

	var lastErr error
	for attempt := 0; attempt <= c.config.MaxRetries; attempt++ {
		if attempt > 0 {
			select {
			case <-ctx.Done():
				return ctx.Err()
			case <-time.After(c.config.RetryDelay * time.Duration(attempt)):
			}
		}

		req, err := http.NewRequestWithContext(ctx, method, url, bodyReader)
		if err != nil {
			lastErr = fmt.Errorf("failed to create request: %w", err)
			continue
		}

		req.Header.Set("Content-Type", "application/json")
		req.Header.Set("User-Agent", c.config.UserAgent)

		resp, err := c.httpClient.Do(req)
		if err != nil {
			lastErr = fmt.Errorf("request failed: %w", err)
			continue
		}

		defer resp.Body.Close()

		if resp.StatusCode >= 200 && resp.StatusCode < 300 {
			if responseBody != nil {
				if err := json.NewDecoder(resp.Body).Decode(responseBody); err != nil {
					return fmt.Errorf("failed to decode response: %w", err)
				}
			}
			return nil
		}

		// Read error response
		errorBody, _ := io.ReadAll(resp.Body)
		lastErr = fmt.Errorf("HTTP %d: %s", resp.StatusCode, string(errorBody))

		// Don't retry on client errors (4xx)
		if resp.StatusCode >= 400 && resp.StatusCode < 500 {
			break
		}
	}

	return fmt.Errorf("request failed after %d attempts: %w", c.config.MaxRetries+1, lastErr)
}

// calculateQualityMetrics computes overall quality metrics from artifacts
func (c *SpecKitClient) calculateQualityMetrics(artifacts []SpecKitArtifact) map[string]float64 {
	metrics := map[string]float64{}

	if len(artifacts) == 0 {
		return metrics
	}

	var totalQuality float64
	for _, artifact := range artifacts {
		totalQuality += artifact.Quality
		metrics[artifact.Type+"_quality"] = artifact.Quality
	}

	metrics["overall_quality"] = totalQuality / float64(len(artifacts))
	metrics["artifact_count"] = float64(len(artifacts))
	metrics["completeness"] = float64(len(artifacts)) / 5.0 // 5 total possible phases

	return metrics
}