package proof

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

const (
	starterSelectedImageSnapshotFileName   = "starter-selected-image-snapshot.json"
	starterSelectedImageSnapshotAPIVersion = "cp9.starter.selected-images/v1alpha1"
	starterSelectedImageSnapshotKind       = "StarterSelectedImageSnapshot"
	starterStatusEvidenceFileName          = "starter-status.json"
	starterStatusEvidenceAPIVersion        = "cp9.starter.status/v1alpha1"
	starterStatusEvidenceKind              = "StarterStatus"
	sourceBackedEvidenceDirName            = "evidence"
)

var (
	errStarterActivationEvidenceNotFound = errors.New("starter activation evidence not found")
	errStarterStatusEvidenceNotFound     = errors.New("starter status evidence not found")
)

func StarterSelectedImageSnapshotPath(stateDir string) string {
	return filepath.Join(stateDir, "reports", sourceBackedEvidenceDirName, starterSelectedImageSnapshotFileName)
}

func StarterStatusEvidencePath(stateDir string) string {
	return filepath.Join(stateDir, "reports", sourceBackedEvidenceDirName, starterStatusEvidenceFileName)
}

type rawStarterActivationEvidence struct {
	APIVersion           string                       `json:"apiVersion"`
	Kind                 string                       `json:"kind"`
	SourceSummaryPath    string                       `json:"source_summary_path"`
	SourceSummaryLoaded  bool                         `json:"source_summary_loaded"`
	SourceBuildServices  []string                     `json:"source_build_services"`
	ActivationImages     []StarterActivationImage     `json:"activation_images"`
	SelectedSourceImages []StarterSelectedSourceImage `json:"selected_source_images"`
}

func LoadStarterActivationEvidence(path string) (*StarterActivationEvidence, error) {
	data, err := os.ReadFile(path)
	if err != nil {
		if errors.Is(err, os.ErrNotExist) {
			return nil, fmt.Errorf("%w: %s", errStarterActivationEvidenceNotFound, path)
		}
		return nil, fmt.Errorf("read %s: %w", path, err)
	}
	var raw rawStarterActivationEvidence
	if err := json.Unmarshal(data, &raw); err != nil {
		return nil, fmt.Errorf("%s: %w", path, err)
	}
	if strings.TrimSpace(raw.APIVersion) != starterSelectedImageSnapshotAPIVersion {
		return nil, fmt.Errorf("%s: apiVersion = %q", path, raw.APIVersion)
	}
	if strings.TrimSpace(raw.Kind) != starterSelectedImageSnapshotKind {
		return nil, fmt.Errorf("%s: kind = %q", path, raw.Kind)
	}
	return &StarterActivationEvidence{
		Status:               StatusPassed,
		ArtifactPath:         path,
		SourceSummaryPath:    strings.TrimSpace(raw.SourceSummaryPath),
		SourceSummaryLoaded:  raw.SourceSummaryLoaded,
		SourceBuildServices:  append([]string(nil), raw.SourceBuildServices...),
		ActivationImages:     append([]StarterActivationImage(nil), raw.ActivationImages...),
		SelectedSourceImages: append([]StarterSelectedSourceImage(nil), raw.SelectedSourceImages...),
	}, nil
}

type rawStarterStatusEvidence struct {
	APIVersion string                  `json:"apiVersion"`
	Kind       string                  `json:"kind"`
	Result     *rawStarterStatusResult `json:"result"`
}

type rawStarterStatusResult struct {
	State            string                       `json:"state"`
	Session          string                       `json:"session"`
	Persona          string                       `json:"persona"`
	RemoteEKSCluster string                       `json:"remote_eks_cluster"`
	Message          string                       `json:"message"`
	Checks           []StarterStatusCheckEvidence `json:"checks"`
}

func LoadStarterStatusEvidence(path string) (*StarterStatusEvidence, error) {
	data, err := os.ReadFile(path)
	if err != nil {
		if errors.Is(err, os.ErrNotExist) {
			return nil, fmt.Errorf("%w: %s", errStarterStatusEvidenceNotFound, path)
		}
		return nil, fmt.Errorf("read %s: %w", path, err)
	}
	var raw rawStarterStatusEvidence
	if err := json.Unmarshal(data, &raw); err != nil {
		return nil, fmt.Errorf("%s: %w", path, err)
	}
	if strings.TrimSpace(raw.APIVersion) != starterStatusEvidenceAPIVersion {
		return nil, fmt.Errorf("%s: apiVersion = %q", path, raw.APIVersion)
	}
	if strings.TrimSpace(raw.Kind) != starterStatusEvidenceKind {
		return nil, fmt.Errorf("%s: kind = %q", path, raw.Kind)
	}
	if raw.Result == nil {
		return nil, fmt.Errorf("%s: result is required", path)
	}
	out := &StarterStatusEvidence{
		Status:           StatusNotCollected,
		ArtifactPath:     path,
		State:            strings.TrimSpace(raw.Result.State),
		Session:          strings.TrimSpace(raw.Result.Session),
		Persona:          strings.TrimSpace(raw.Result.Persona),
		RemoteEKSCluster: strings.TrimSpace(raw.Result.RemoteEKSCluster),
		Message:          strings.TrimSpace(raw.Result.Message),
	}
	for _, check := range raw.Result.Checks {
		if !isStarterSourceImageCheck(check.Name) {
			continue
		}
		check.Name = strings.TrimSpace(check.Name)
		check.Status = statusFromStarterCheck(check.Status)
		check.Message = strings.TrimSpace(check.Message)
		out.SourceImageChecks = append(out.SourceImageChecks, check)
	}
	if len(out.SourceImageChecks) == 0 {
		return out, nil
	}
	out.Status = StatusPassed
	for _, check := range out.SourceImageChecks {
		if check.Status != StatusPassed {
			out.Status = StatusPartial
			break
		}
	}
	return out, nil
}

func isStarterSourceImageCheck(name string) bool {
	name = strings.TrimSpace(name)
	return name == "starter-selected-image-snapshot" || (strings.HasPrefix(name, "starter-") && strings.HasSuffix(name, "-source-image"))
}

func statusFromStarterCheck(status string) string {
	switch strings.TrimSpace(status) {
	case StatusPassed:
		return StatusPassed
	case "not_ready", StatusFailed, StatusPartial:
		return StatusPartial
	case "", StatusNotCollected:
		return StatusNotCollected
	default:
		return StatusPartial
	}
}
