package config

import (
	"encoding/json"
	"fmt"
	"os"
	"path/filepath"
	"strings"
)

// SaveTarget adds or replaces a named ForwardTarget in the config file at path.
// Only JSON config files are written in-place. For other formats an error is
// returned describing what to add manually.
func SaveTarget(path, name string, target ForwardTarget) error {
	ext := strings.ToLower(strings.TrimPrefix(filepath.Ext(path), "."))

	data, err := os.ReadFile(path)
	if err != nil {
		return fmt.Errorf("read config %s: %w", path, err)
	}

	switch ext {
	case "json":
		return saveTargetJSON(path, data, name, target)
	default:
		snippet, _ := json.MarshalIndent(map[string]ForwardTarget{name: target}, "", "  ")
		return fmt.Errorf(
			"auto-update not supported for .%s files\n"+
				"Add the following to the 'forward.targets' section of %s:\n\n%s",
			ext, path, snippet,
		)
	}
}

// RemoveBrokenEndpoints removes failing endpoints from the config file.
// broken maps target name → set of failing endpoint URLs.
// If all endpoints of a target are removed, the target itself is deleted.
// Returns the list of removed items as human-readable strings.
func RemoveBrokenEndpoints(path string, broken map[string][]string) ([]string, error) {
	ext := strings.ToLower(strings.TrimPrefix(filepath.Ext(path), "."))
	if ext != "json" {
		return nil, fmt.Errorf("auto-update not supported for .%s files; edit %s manually", ext, path)
	}

	data, err := os.ReadFile(path)
	if err != nil {
		return nil, fmt.Errorf("read config %s: %w", path, err)
	}

	var cfg Config
	if err := json.Unmarshal(data, &cfg); err != nil {
		return nil, fmt.Errorf("parse %s: %w", path, err)
	}

	var removed []string

	for targetName, failedURLs := range broken {
		target, ok := cfg.Forward.Targets[targetName]
		if !ok {
			continue
		}

		failSet := make(map[string]bool, len(failedURLs))
		for _, u := range failedURLs {
			failSet[u] = true
		}

		kept := target.Endpoints[:0]
		for _, ep := range target.Endpoints {
			if failSet[ep.URL] {
				removed = append(removed, fmt.Sprintf("endpoint %s (target %q)", ep.URL, targetName))
			} else {
				kept = append(kept, ep)
			}
		}

		if len(kept) == 0 {
			delete(cfg.Forward.Targets, targetName)
			removed = append(removed, fmt.Sprintf("target %q (all endpoints failed)", targetName))
			if cfg.Forward.Default == targetName {
				cfg.Forward.Default = ""
			}
		} else {
			target.Endpoints = kept
			cfg.Forward.Targets[targetName] = target
		}
	}

	if len(removed) == 0 {
		return nil, nil
	}

	out, err := json.MarshalIndent(cfg, "", "  ")
	if err != nil {
		return nil, fmt.Errorf("marshal config: %w", err)
	}
	if err := os.WriteFile(path, append(out, '\n'), 0o600); err != nil {
		return nil, fmt.Errorf("write %s: %w", path, err)
	}
	return removed, nil
}

// WriteConfig serialises cfg as indented JSON and atomically overwrites path.
func WriteConfig(path string, cfg Config) error {
	out, err := json.MarshalIndent(cfg, "", "  ")
	if err != nil {
		return fmt.Errorf("marshal config: %w", err)
	}
	if err := os.WriteFile(path, append(out, '\n'), 0o600); err != nil {
		return fmt.Errorf("write %s: %w", path, err)
	}
	return nil
}

func saveTargetJSON(path string, data []byte, name string, target ForwardTarget) error {
	var cfg Config
	if err := json.Unmarshal(data, &cfg); err != nil {
		return fmt.Errorf("parse %s: %w", path, err)
	}

	if cfg.Forward.Targets == nil {
		cfg.Forward.Targets = make(map[string]ForwardTarget)
	}
	cfg.Forward.Targets[name] = target
	if cfg.Forward.Default == "" {
		cfg.Forward.Default = name
	}

	out, err := json.MarshalIndent(cfg, "", "  ")
	if err != nil {
		return fmt.Errorf("marshal config: %w", err)
	}
	if err := os.WriteFile(path, append(out, '\n'), 0o600); err != nil {
		return fmt.Errorf("write %s: %w", path, err)
	}
	return nil
}
