package tasks

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

	"github.com/chorus-services/whoosh/internal/gitea"
	"github.com/rs/zerolog/log"
)

// GiteaIntegration handles synchronization with GITEA issues
type GiteaIntegration struct {
	taskService *Service
	giteaClient *gitea.Client
	config      *GiteaConfig
}

// GiteaConfig contains GITEA integration configuration
type GiteaConfig struct {
	BaseURL      string            `json:"base_url"`
	TaskLabel    string            `json:"task_label"`    // e.g., "bzzz-task"
	Repositories []string          `json:"repositories"`  // repositories to monitor
	TeamMapping  map[string]string `json:"team_mapping"`  // label -> team mapping
}

// NewGiteaIntegration creates a new GITEA integration
func NewGiteaIntegration(taskService *Service, giteaClient *gitea.Client, config *GiteaConfig) *GiteaIntegration {
	if config == nil {
		config = &GiteaConfig{
			TaskLabel:    "bzzz-task",
			Repositories: []string{},
			TeamMapping:  make(map[string]string),
		}
	}
	
	return &GiteaIntegration{
		taskService: taskService,
		giteaClient: giteaClient,
		config:      config,
	}
}

// GiteaIssue represents a GITEA issue response
type GiteaIssue struct {
	ID          int       `json:"id"`
	Number      int       `json:"number"`
	Title       string    `json:"title"`
	Body        string    `json:"body"`
	State       string    `json:"state"` // "open", "closed"
	URL         string    `json:"html_url"`
	Labels      []GiteaLabel `json:"labels"`
	Repository  GiteaRepo    `json:"repository"`
	CreatedAt   time.Time `json:"created_at"`
	UpdatedAt   time.Time `json:"updated_at"`
	Assignees   []GiteaUser  `json:"assignees"`
}

type GiteaLabel struct {
	Name        string `json:"name"`
	Color       string `json:"color"`
	Description string `json:"description"`
}

type GiteaRepo struct {
	FullName string `json:"full_name"`
	HTMLURL  string `json:"html_url"`
}

type GiteaUser struct {
	ID       int    `json:"id"`
	Login    string `json:"login"`
	FullName string `json:"full_name"`
}

// SyncIssuesFromGitea fetches issues from GITEA and creates/updates tasks
func (g *GiteaIntegration) SyncIssuesFromGitea(ctx context.Context, repository string) error {
	log.Info().
		Str("repository", repository).
		Msg("Starting GITEA issue sync")
	
	// Fetch issues from GITEA API
	issues, err := g.fetchIssuesFromGitea(ctx, repository)
	if err != nil {
		return fmt.Errorf("failed to fetch GITEA issues: %w", err)
	}
	
	syncedCount := 0
	errorCount := 0
	
	for _, issue := range issues {
		// Check if issue has task label
		if !g.hasTaskLabel(issue) {
			continue
		}
		
		err := g.syncIssue(ctx, issue)
		if err != nil {
			log.Error().Err(err).
				Int("issue_id", issue.ID).
				Str("repository", repository).
				Msg("Failed to sync issue")
			errorCount++
			continue
		}
		
		syncedCount++
	}
	
	log.Info().
		Str("repository", repository).
		Int("synced", syncedCount).
		Int("errors", errorCount).
		Msg("GITEA issue sync completed")
	
	return nil
}

// SyncIssue synchronizes a single GITEA issue with the task system
func (g *GiteaIntegration) syncIssue(ctx context.Context, issue GiteaIssue) error {
	externalID := fmt.Sprintf("%d", issue.ID)
	
	// Check if task already exists
	existingTask, err := g.taskService.GetTaskByExternalID(ctx, externalID, SourceTypeGitea)
	if err != nil && !strings.Contains(err.Error(), "not found") {
		return fmt.Errorf("failed to check existing task: %w", err)
	}
	
	if existingTask != nil {
		// Update existing task
		return g.updateTaskFromIssue(ctx, existingTask, issue)
	} else {
		// Create new task
		return g.createTaskFromIssue(ctx, issue)
	}
}

// createTaskFromIssue creates a new task from a GITEA issue
func (g *GiteaIntegration) createTaskFromIssue(ctx context.Context, issue GiteaIssue) error {
	labels := make([]string, len(issue.Labels))
	for i, label := range issue.Labels {
		labels[i] = label.Name
	}
	
	// Determine priority from labels
	priority := g.determinePriorityFromLabels(labels)
	
	// Extract estimated hours from issue body (look for patterns like "Estimated: 4 hours")
	estimatedHours := g.extractEstimatedHours(issue.Body)
	
	input := &CreateTaskInput{
		ExternalID:        fmt.Sprintf("%d", issue.ID),
		ExternalURL:       issue.URL,
		SourceType:        SourceTypeGitea,
		SourceConfig: map[string]interface{}{
			"gitea_number":  issue.Number,
			"repository":    issue.Repository.FullName,
			"assignees":     issue.Assignees,
		},
		Title:             issue.Title,
		Description:       issue.Body,
		Priority:          priority,
		Repository:        issue.Repository.FullName,
		Labels:            labels,
		EstimatedHours:    estimatedHours,
		ExternalCreatedAt: &issue.CreatedAt,
		ExternalUpdatedAt: &issue.UpdatedAt,
	}
	
	task, err := g.taskService.CreateTask(ctx, input)
	if err != nil {
		return fmt.Errorf("failed to create task from GITEA issue: %w", err)
	}
	
	log.Info().
		Str("task_id", task.ID.String()).
		Int("gitea_issue_id", issue.ID).
		Str("repository", issue.Repository.FullName).
		Msg("Created task from GITEA issue")
	
	return nil
}

// updateTaskFromIssue updates an existing task from a GITEA issue
func (g *GiteaIntegration) updateTaskFromIssue(ctx context.Context, task *Task, issue GiteaIssue) error {
	// Check if issue was updated since last sync
	if task.ExternalUpdatedAt != nil && !issue.UpdatedAt.After(*task.ExternalUpdatedAt) {
		return nil // No updates needed
	}
	
	// Determine new status based on GITEA state
	var newStatus TaskStatus
	switch issue.State {
	case "open":
		if task.Status == TaskStatusClosed {
			newStatus = TaskStatusOpen
		}
	case "closed":
		if task.Status != TaskStatusClosed {
			newStatus = TaskStatusClosed
		}
	}
	
	// Update status if changed
	if newStatus != "" && newStatus != task.Status {
		update := &TaskStatusUpdate{
			TaskID: task.ID,
			Status: newStatus,
			Reason: fmt.Sprintf("GITEA issue state changed to %s", issue.State),
		}
		
		err := g.taskService.UpdateTaskStatus(ctx, update)
		if err != nil {
			return fmt.Errorf("failed to update task status: %w", err)
		}
		
		log.Info().
			Str("task_id", task.ID.String()).
			Int("gitea_issue_id", issue.ID).
			Str("old_status", string(task.Status)).
			Str("new_status", string(newStatus)).
			Msg("Updated task status from GITEA issue")
	}
	
	// TODO: Update other fields like title, description, labels if needed
	// This would require additional database operations
	
	return nil
}

// ProcessGiteaWebhook processes a GITEA webhook payload
func (g *GiteaIntegration) ProcessGiteaWebhook(ctx context.Context, payload []byte) error {
	var webhookData struct {
		Action     string     `json:"action"`
		Issue      GiteaIssue `json:"issue"`
		Repository GiteaRepo  `json:"repository"`
	}
	
	if err := json.Unmarshal(payload, &webhookData); err != nil {
		return fmt.Errorf("failed to parse GITEA webhook payload: %w", err)
	}
	
	// Only process issues with task label
	if !g.hasTaskLabel(webhookData.Issue) {
		log.Debug().
			Int("issue_id", webhookData.Issue.ID).
			Str("action", webhookData.Action).
			Msg("Ignoring GITEA issue without task label")
		return nil
	}
	
	log.Info().
		Str("action", webhookData.Action).
		Int("issue_id", webhookData.Issue.ID).
		Str("repository", webhookData.Repository.FullName).
		Msg("Processing GITEA webhook")
	
	switch webhookData.Action {
	case "opened", "edited", "reopened", "closed":
		return g.syncIssue(ctx, webhookData.Issue)
	case "labeled", "unlabeled":
		// Re-sync to update task labels and tech stack
		return g.syncIssue(ctx, webhookData.Issue)
	default:
		log.Debug().
			Str("action", webhookData.Action).
			Msg("Ignoring GITEA webhook action")
		return nil
	}
}

// Helper methods

func (g *GiteaIntegration) fetchIssuesFromGitea(ctx context.Context, repository string) ([]GiteaIssue, error) {
	// This would make actual HTTP calls to GITEA API
	// For MVP, we'll return mock data based on known structure
	
	// In production, this would be:
	// url := fmt.Sprintf("%s/repos/%s/issues", g.config.BaseURL, repository)
	// resp, err := g.giteaClient.Get(url)
	// ... parse response
	
	// Mock issues for testing
	mockIssues := []GiteaIssue{
		{
			ID:     123,
			Number: 1,
			Title:  "Implement user authentication system",
			Body:   "Add JWT-based authentication with login and registration endpoints\n\n- JWT token generation\n- User registration\n- Password hashing\n\nEstimated: 8 hours",
			State:  "open",
			URL:    fmt.Sprintf("https://gitea.chorus.services/%s/issues/1", repository),
			Labels: []GiteaLabel{
				{Name: "bzzz-task", Color: "0052cc"},
				{Name: "backend", Color: "1d76db"},
				{Name: "high-priority", Color: "d93f0b"},
			},
			Repository: GiteaRepo{FullName: repository},
			CreatedAt:  time.Now().Add(-24 * time.Hour),
			UpdatedAt:  time.Now().Add(-2 * time.Hour),
		},
		{
			ID:     124,
			Number: 2,
			Title:  "Fix database connection pooling",
			Body:   "Connection pool is not releasing connections properly under high load\n\nSteps to reproduce:\n1. Start application\n2. Generate high load\n3. Monitor connection count",
			State:  "open", 
			URL:    fmt.Sprintf("https://gitea.chorus.services/%s/issues/2", repository),
			Labels: []GiteaLabel{
				{Name: "bzzz-task", Color: "0052cc"},
				{Name: "database", Color: "5319e7"},
				{Name: "bug", Color: "d93f0b"},
			},
			Repository: GiteaRepo{FullName: repository},
			CreatedAt:  time.Now().Add(-12 * time.Hour),
			UpdatedAt:  time.Now().Add(-1 * time.Hour),
		},
	}
	
	log.Debug().
		Str("repository", repository).
		Int("mock_issues", len(mockIssues)).
		Msg("Returning mock GITEA issues for MVP")
	
	return mockIssues, nil
}

func (g *GiteaIntegration) hasTaskLabel(issue GiteaIssue) bool {
	for _, label := range issue.Labels {
		if label.Name == g.config.TaskLabel {
			return true
		}
	}
	return false
}

func (g *GiteaIntegration) determinePriorityFromLabels(labels []string) TaskPriority {
	for _, label := range labels {
		switch strings.ToLower(label) {
		case "critical", "urgent", "critical-priority":
			return TaskPriorityCritical
		case "high", "high-priority", "important":
			return TaskPriorityHigh
		case "low", "low-priority", "minor":
			return TaskPriorityLow
		}
	}
	return TaskPriorityMedium
}

func (g *GiteaIntegration) extractEstimatedHours(body string) int {
	// Look for patterns like "Estimated: 4 hours", "Est: 8h", etc.
	lines := strings.Split(strings.ToLower(body), "\n")
	for _, line := range lines {
		if strings.Contains(line, "estimated:") || strings.Contains(line, "est:") {
			// Extract number from line
			words := strings.Fields(line)
			for i, word := range words {
				if (word == "estimated:" || word == "est:") && i+1 < len(words) {
					if hours, err := strconv.Atoi(strings.TrimSuffix(words[i+1], "h")); err == nil {
						return hours
					}
				}
			}
		}
	}
	return 0
}