package storage

import (
	"encoding/json"
	"fmt"
	"os"
	"path/filepath"
	"sync"

	"llm_processor/models"
)

var (
	criteriaPath = "criteria.txt"
	criteriaMux  sync.RWMutex
)

// SetCriteriaPath sets the path for the criteria file
func SetCriteriaPath(path string) {
	criteriaMux.Lock()
	defer criteriaMux.Unlock()
	criteriaPath = path
}

// LoadPapers loads papers from a JSON file
func LoadPapers(filePath string) ([]models.Paper, error) {
	absPath, err := filepath.Abs(filePath)
	if err != nil {
		return nil, fmt.Errorf("failed to resolve absolute path: %w", err)
	}

	file, err := os.ReadFile(absPath)
	if err != nil {
		return nil, fmt.Errorf("failed to read papers file: %w", err)
	}

	var papers []models.Paper
	if err := json.Unmarshal(file, &papers); err != nil {
		return nil, fmt.Errorf("failed to parse papers JSON: %w", err)
	}

	return papers, nil
}

// LoadCriteria loads evaluation criteria from a text file
func LoadCriteria() (string, error) {
	criteriaMux.RLock()
	filePath := criteriaPath
	criteriaMux.RUnlock()

	content, err := os.ReadFile(filePath)
	if err != nil {
		return "", fmt.Errorf("failed to read criteria file: %w", err)
	}
	return string(content), nil
}

// InitializeResultsFile creates a new results file with empty categories
func InitializeResultsFile(filePath string) error {
	results := models.OrganizedResults{
		Accepted: []models.Result{},
		Rejected: []models.Result{},
		Errors:   []models.Result{},
	}

	return saveToFile(filePath, results)
}

// SaveResult appends a result to the appropriate category in the results file
func SaveResult(result models.Result, filePath string) error {
	// Read existing results
	results, err := loadResults(filePath)
	if err != nil {
		return fmt.Errorf("failed to load results: %w", err)
	}

	// Append to appropriate category
	switch result.Decision {
	case "ACCEPT":
		results.Accepted = append(results.Accepted, result)
	case "REJECT":
		results.Rejected = append(results.Rejected, result)
	default:
		results.Errors = append(results.Errors, result)
	}

	// Save updated results
	if err := saveToFile(filePath, results); err != nil {
		// Try fallback save
		if fallbackErr := saveFallback(result, filePath); fallbackErr != nil {
			return fmt.Errorf("failed to save results: %w (fallback also failed: %v)", err, fallbackErr)
		}
		return nil
	}

	return nil
}

func loadResults(filePath string) (*models.OrganizedResults, error) {
	file, err := os.ReadFile(filePath)
	if err != nil {
		return nil, fmt.Errorf("failed to read results file: %w", err)
	}

	var results models.OrganizedResults
	if err := json.Unmarshal(file, &results); err != nil {
		return nil, fmt.Errorf("failed to parse results JSON: %w", err)
	}

	return &results, nil
}

func saveToFile(filePath string, data interface{}) error {
	file, err := os.Create(filePath)
	if err != nil {
		return fmt.Errorf("failed to create file: %w", err)
	}
	defer file.Close()

	encoder := json.NewEncoder(file)
	encoder.SetIndent("", "  ")
	if err := encoder.Encode(data); err != nil {
		return fmt.Errorf("failed to encode JSON: %w", err)
	}

	return nil
}

func saveFallback(result models.Result, originalPath string) error {
	fallbackPath := originalPath + ".fallback"
	file, err := os.OpenFile(fallbackPath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
	if err != nil {
		return fmt.Errorf("failed to open fallback file: %w", err)
	}
	defer file.Close()

	encoder := json.NewEncoder(file)
	if err := encoder.Encode(result); err != nil {
		return fmt.Errorf("failed to encode result to fallback file: %w", err)
	}

	return nil
}
