package ide

import (
	"encoding/json"
	"fmt"
	"os"
	"path/filepath"
	"skene/internal/services/syscheck"
	"strings"
	"time"
)

// IDEIssue represents an issue that can be sent to the IDE
type IDEIssue struct {
	Type         string                 `json:"type"`
	Title        string                 `json:"title"`
	Description  string                 `json:"description"`
	FailedChecks []FailedCheck          `json:"failed_checks"`
	Timestamp    string                 `json:"timestamp"`
	Context      map[string]interface{} `json:"context,omitempty"`
}

// FailedCheck represents a failed system check
type FailedCheck struct {
	Name       string `json:"name"`
	Message    string `json:"message"`
	FixCommand string `json:"fix_command"`
	FixURL     string `json:"fix_url,omitempty"`
	Required   bool   `json:"required"`
}

// Communicator handles communication with the IDE
type Communicator struct {
	workspacePath string
}

// NewCommunicator creates a new IDE communicator
func NewCommunicator(workspacePath string) *Communicator {
	return &Communicator{
		workspacePath: workspacePath,
	}
}

// SendSystemCheckIssues sends failed system check results to the IDE
func (c *Communicator) SendSystemCheckIssues(results *syscheck.SystemCheckResult) error {
	var failedChecks []FailedCheck

	if results.UV.Status == syscheck.StatusFailed {
		alternatives := []string{
			"curl -LsSf https://astral.sh/uv/install.sh | sh",
			"brew install uv",
			"pip install uv",
		}
		fixCmd := "Auto-provisioning failed. You can install uv manually:\n" + strings.Join(alternatives, "\n")

		failedChecks = append(failedChecks, FailedCheck{
			Name:       results.UV.Name,
			Message:    results.UV.Message,
			FixCommand: fixCmd,
			Required:   results.UV.Required,
		})
	}

	if len(failedChecks) == 0 {
		return fmt.Errorf("no failed checks to send")
	}

	issue := IDEIssue{
		Type:         "system_check",
		Title:        "System Prerequisites Check Failed",
		Description:  "The CLI failed to auto-provision the uvx runtime needed to run Skene libraries.",
		FailedChecks: failedChecks,
		Timestamp:    time.Now().Format(time.RFC3339),
		Context: map[string]interface{}{
			"can_proceed": results.CanProceed,
			"all_passed":  results.AllPassed,
		},
	}

	return c.writeIssueToFile(issue)
}

func (c *Communicator) writeIssueToFile(issue IDEIssue) error {
	jsonLocation := filepath.Join(c.workspacePath, ".cursor", "skene-request.json")
	mdLocation := filepath.Join(c.workspacePath, ".cursor", "skene-request.md")

	dir := filepath.Dir(jsonLocation)
	if err := os.MkdirAll(dir, 0755); err != nil {
		return fmt.Errorf("failed to create .cursor directory: %w", err)
	}

	data, err := json.MarshalIndent(issue, "", "  ")
	if err != nil {
		return fmt.Errorf("failed to marshal JSON: %w", err)
	}

	if err := os.WriteFile(jsonLocation, data, 0644); err != nil {
		return fmt.Errorf("failed to write JSON file: %w", err)
	}

	mdContent := c.generateMarkdown(issue)
	_ = os.WriteFile(mdLocation, []byte(mdContent), 0644)

	fmt.Fprintf(os.Stderr, "\n[CURSOR_REQUEST] File written to: %s\n", jsonLocation)
	fmt.Fprintf(os.Stderr, "[CURSOR_REQUEST] Type: %s\n", issue.Type)
	fmt.Fprintf(os.Stderr, "[CURSOR_REQUEST] Title: %s\n", issue.Title)

	return nil
}

func (c *Communicator) generateMarkdown(issue IDEIssue) string {
	var md strings.Builder

	md.WriteString(fmt.Sprintf("# %s\n\n", issue.Title))
	md.WriteString(fmt.Sprintf("%s\n\n", issue.Description))
	md.WriteString(fmt.Sprintf("**Type:** `%s`  \n", issue.Type))
	md.WriteString(fmt.Sprintf("**Timestamp:** %s\n\n", issue.Timestamp))

	md.WriteString("## Failed Checks\n\n")
	for i, check := range issue.FailedChecks {
		md.WriteString(fmt.Sprintf("### %d. %s\n\n", i+1, check.Name))
		md.WriteString(fmt.Sprintf("- **Message:** %s\n", check.Message))
		md.WriteString(fmt.Sprintf("- **Required:** %v\n", check.Required))

		if check.FixCommand != "" {
			commands := strings.Split(check.FixCommand, "\n")
			md.WriteString("- **Fix Commands:**\n")
			for _, cmd := range commands {
				cmd = strings.TrimSpace(cmd)
				if cmd != "" {
					if strings.HasPrefix(cmd, "Alternative") {
						md.WriteString(fmt.Sprintf("  **%s**\n", cmd))
					} else {
						md.WriteString(fmt.Sprintf("  ```bash\n  %s\n  ```\n", cmd))
					}
				}
			}
		}

		if check.FixURL != "" {
			md.WriteString(fmt.Sprintf("- **More Info:** %s\n", check.FixURL))
		}

		md.WriteString("\n")
	}

	md.WriteString("\n---\n\n")
	md.WriteString("*This file was automatically generated by skene-cli.*\n")

	return md.String()
}

// GetRequestFilePath returns the path where the request file was written
func (c *Communicator) GetRequestFilePath() string {
	locations := []string{
		filepath.Join(c.workspacePath, ".cursor", "skene-request.json"),
		filepath.Join(c.workspacePath, ".skene", "ide-request.json"),
		filepath.Join(os.TempDir(), "skene-ide-request.json"),
	}

	for _, location := range locations {
		if _, err := os.Stat(location); err == nil {
			return location
		}
	}

	return ""
}
