package fixers

import (
	"context"
	"fmt"
	"go/ast"
	"go/format"
	"go/parser"
	"go/token"
	"os"
	"strings"

	"github.com/yourorg/devour/internal/quality"
	"github.com/yourorg/devour/internal/quality/plugins"
)

type UnusedImportFixer struct{}

func NewUnusedImportFixer() *UnusedImportFixer {
	return &UnusedImportFixer{}
}

func (f *UnusedImportFixer) Name() string {
	return "unused_import"
}

func (f *UnusedImportFixer) Description() string {
	return "Removes unused import statements"
}

func (f *UnusedImportFixer) CanFix(finding quality.Finding) bool {
	return finding.Type == "unused_import"
}

func (f *UnusedImportFixer) Fix(ctx context.Context, finding quality.Finding, dryRun bool) (*plugins.FixResult, error) {
	fset := token.NewFileSet()
	node, err := parser.ParseFile(fset, finding.File, nil, parser.ParseComments)
	if err != nil {
		return nil, fmt.Errorf("parse error: %w", err)
	}

	importToRemove := finding.Metadata["import_path"]
	if importToRemove == "" {
		return nil, fmt.Errorf("no import_path in finding metadata")
	}

	var newImports []*ast.ImportSpec
	for _, imp := range node.Imports {
		path := strings.Trim(imp.Path.Value, `"`)
		if path != importToRemove {
			newImports = append(newImports, imp)
		}
	}

	node.Imports = newImports

	if dryRun {
		return &plugins.FixResult{
			Success: true,
			Message: fmt.Sprintf("Would remove import '%s' from %s", importToRemove, finding.File),
		}, nil
	}

	var output strings.Builder
	if err := format.Node(&output, fset, node); err != nil {
		return nil, fmt.Errorf("format error: %w", err)
	}

	if err := os.WriteFile(finding.File, []byte(output.String()), 0644); err != nil {
		return nil, fmt.Errorf("write error: %w", err)
	}

	return &plugins.FixResult{
		Success: true,
		Message: fmt.Sprintf("Removed unused import '%s' from %s", importToRemove, finding.File),
	}, nil
}

type FormattingFixer struct{}

func NewFormattingFixer() *FormattingFixer {
	return &FormattingFixer{}
}

func (f *FormattingFixer) Name() string {
	return "format"
}

func (f *FormattingFixer) Description() string {
	return "Formats Go source files using gofmt style"
}

func (f *FormattingFixer) CanFix(finding quality.Finding) bool {
	return finding.Type == "formatting" || finding.Type == "style"
}

func (f *FormattingFixer) Fix(ctx context.Context, finding quality.Finding, dryRun bool) (*plugins.FixResult, error) {
	fset := token.NewFileSet()
	node, err := parser.ParseFile(fset, finding.File, nil, parser.ParseComments)
	if err != nil {
		return nil, fmt.Errorf("parse error: %w", err)
	}

	if dryRun {
		return &plugins.FixResult{
			Success: true,
			Message: fmt.Sprintf("Would format %s", finding.File),
		}, nil
	}

	var output strings.Builder
	if err := format.Node(&output, fset, node); err != nil {
		return nil, fmt.Errorf("format error: %w", err)
	}

	if err := os.WriteFile(finding.File, []byte(output.String()), 0644); err != nil {
		return nil, fmt.Errorf("write error: %w", err)
	}

	return &plugins.FixResult{
		Success: true,
		Message: fmt.Sprintf("Formatted %s", finding.File),
	}, nil
}
