// Package infra gère l'infrastructure (serveurs, containers, nginx).
package infra

import (
	"context"
	"encoding/json"
	"fmt"
	"strings"
)

// IncusContainer représente un container Incus retourné par incus list.
type IncusContainer struct {
	Name       string   `json:"name"`
	State      string   `json:"state"`
	IPv4       []string `json:"ipv4"`
	IPv6       []string `json:"ipv6"`
	Type       string   `json:"type"`
	Snapshots  int      `json:"snapshots"`
	Location   string   `json:"location"`
	Image      string   `json:"-"` // Rempli séparément
	CreatedAt  string   `json:"created_at"`
}

// incusListJSON représente le format JSON de incus list.
type incusListJSON struct {
	Name      string `json:"name"`
	Status    string `json:"status"`
	Type      string `json:"type"`
	Snapshots int    `json:"snapshots"`
	Location  string `json:"location"`
	IPv4      string `json:"ipv4"`
	IPv6      string `json:"ipv6"`
}

// ListIncusContainers liste les containers Incus sur le serveur.
func (c *SSHClient) ListIncusContainers(ctx context.Context) ([]IncusContainer, error) {
	// Récupérer la liste en format JSON
	result, err := c.Exec(ctx, "incus list --format json")
	if err != nil {
		return nil, fmt.Errorf("incus list: %w", err)
	}
	if result.ExitCode != 0 {
		return nil, fmt.Errorf("incus list failed: %s", result.Stderr)
	}

	// Parser le JSON
	var rawContainers []struct {
		Name       string `json:"name"`
		Status     string `json:"status"`
		StatusCode int    `json:"status_code"`
		Type       string `json:"type"`
		Config     struct {
			Image string `json:"image.description"`
		} `json:"config"`
		State struct {
			Network map[string]struct {
				Addresses []struct {
					Address string `json:"address"`
					Family  string `json:"family"`
					Scope   string `json:"scope"`
				} `json:"addresses"`
			} `json:"network"`
		} `json:"state"`
		Snapshots []interface{} `json:"snapshots"`
		Location  string        `json:"location"`
		CreatedAt string        `json:"created_at"`
	}

	if err := json.Unmarshal([]byte(result.Stdout), &rawContainers); err != nil {
		return nil, fmt.Errorf("parse incus json: %w", err)
	}

	// Convertir en notre format
	containers := make([]IncusContainer, 0, len(rawContainers))
	for _, rc := range rawContainers {
		container := IncusContainer{
			Name:      rc.Name,
			State:     strings.ToLower(rc.Status),
			Type:      rc.Type,
			Snapshots: len(rc.Snapshots),
			Location:  rc.Location,
			Image:     rc.Config.Image,
			CreatedAt: rc.CreatedAt,
		}

		// Extraire les IPs
		for _, net := range rc.State.Network {
			for _, addr := range net.Addresses {
				if addr.Scope == "global" {
					if addr.Family == "inet" {
						container.IPv4 = append(container.IPv4, addr.Address)
					} else if addr.Family == "inet6" {
						container.IPv6 = append(container.IPv6, addr.Address)
					}
				}
			}
		}

		containers = append(containers, container)
	}

	return containers, nil
}

// GetIncusContainer récupère les infos d'un container spécifique.
func (c *SSHClient) GetIncusContainer(ctx context.Context, name string) (*IncusContainer, error) {
	containers, err := c.ListIncusContainers(ctx)
	if err != nil {
		return nil, err
	}

	for _, container := range containers {
		if container.Name == name {
			return &container, nil
		}
	}

	return nil, fmt.Errorf("container %s not found", name)
}

// StartIncusContainer démarre un container.
func (c *SSHClient) StartIncusContainer(ctx context.Context, name string) error {
	result, err := c.Exec(ctx, fmt.Sprintf("incus start %s", name))
	if err != nil {
		return fmt.Errorf("incus start: %w", err)
	}
	if result.ExitCode != 0 {
		return fmt.Errorf("incus start %s failed: %s", name, result.Stderr)
	}
	return nil
}

// StopIncusContainer arrête un container.
func (c *SSHClient) StopIncusContainer(ctx context.Context, name string) error {
	result, err := c.Exec(ctx, fmt.Sprintf("incus stop %s", name))
	if err != nil {
		return fmt.Errorf("incus stop: %w", err)
	}
	if result.ExitCode != 0 {
		return fmt.Errorf("incus stop %s failed: %s", name, result.Stderr)
	}
	return nil
}

// RestartIncusContainer redémarre un container.
func (c *SSHClient) RestartIncusContainer(ctx context.Context, name string) error {
	result, err := c.Exec(ctx, fmt.Sprintf("incus restart %s", name))
	if err != nil {
		return fmt.Errorf("incus restart: %w", err)
	}
	if result.ExitCode != 0 {
		return fmt.Errorf("incus restart %s failed: %s", name, result.Stderr)
	}
	return nil
}

// ExecInContainer exécute une commande dans un container.
func (c *SSHClient) ExecInContainer(ctx context.Context, containerName, cmd string) (*SSHResult, error) {
	fullCmd := fmt.Sprintf("incus exec %s -- %s", containerName, cmd)
	return c.Exec(ctx, fullCmd)
}

// ExecInContainerSimple exécute une commande et retourne stdout.
func (c *SSHClient) ExecInContainerSimple(ctx context.Context, containerName, cmd string) (string, error) {
	result, err := c.ExecInContainer(ctx, containerName, cmd)
	if err != nil {
		return "", err
	}
	if result.ExitCode != 0 {
		return "", fmt.Errorf("command failed in %s (exit %d): %s", containerName, result.ExitCode, result.Stderr)
	}
	return strings.TrimSpace(result.Stdout), nil
}

// PushFileToContainer envoie un fichier vers un container.
func (c *SSHClient) PushFileToContainer(ctx context.Context, containerName, localPath, remotePath string) error {
	// D'abord copier vers le serveur hôte
	tempPath := fmt.Sprintf("/tmp/incus-push-%d", ctx.Value("request_id"))
	if err := c.CopyFile(ctx, localPath, tempPath); err != nil {
		return fmt.Errorf("copy to host: %w", err)
	}

	// Puis push vers le container
	result, err := c.Exec(ctx, fmt.Sprintf("incus file push %s %s%s && rm %s",
		tempPath, containerName, remotePath, tempPath))
	if err != nil {
		return fmt.Errorf("incus file push: %w", err)
	}
	if result.ExitCode != 0 {
		return fmt.Errorf("incus file push failed: %s", result.Stderr)
	}

	return nil
}

// PullFileFromContainer récupère un fichier depuis un container.
func (c *SSHClient) PullFileFromContainer(ctx context.Context, containerName, remotePath, localPath string) error {
	tempPath := fmt.Sprintf("/tmp/incus-pull-%d", ctx.Value("request_id"))

	// Pull depuis le container vers l'hôte
	result, err := c.Exec(ctx, fmt.Sprintf("incus file pull %s%s %s",
		containerName, remotePath, tempPath))
	if err != nil {
		return fmt.Errorf("incus file pull: %w", err)
	}
	if result.ExitCode != 0 {
		return fmt.Errorf("incus file pull failed: %s", result.Stderr)
	}

	// Puis copier vers local
	if err := c.CopyFrom(ctx, tempPath, localPath); err != nil {
		return fmt.Errorf("copy from host: %w", err)
	}

	// Nettoyer
	c.Exec(ctx, fmt.Sprintf("rm %s", tempPath))

	return nil
}

// GetContainerLogs récupère les logs d'un container.
func (c *SSHClient) GetContainerLogs(ctx context.Context, containerName string, lines int) (string, error) {
	if lines <= 0 {
		lines = 100
	}
	return c.ExecInContainerSimple(ctx, containerName, fmt.Sprintf("journalctl -n %d --no-pager", lines))
}
