package mockupdateserver

import (
	"archive/zip"
	"bytes"
	"crypto/sha256"
	"encoding/hex"
	"encoding/json"
	"net/http"
	"runtime"

	"github.com/Eriyc/rules_wails/pkg/wails3kit/updates"
)

const BearerToken = "test-token"

func NewHandler() http.Handler {
	mux := http.NewServeMux()
	mux.HandleFunc("/manifest.json", manifest)
	mux.HandleFunc("/artifacts/updater-example_0.2.0.zip", artifact)
	mux.HandleFunc("/artifacts/updater-example_0.2.0.zip.sha256", checksum)

	return http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) {
		if request.Header.Get("Authorization") != "Bearer "+BearerToken {
			http.Error(writer, "unauthorized", http.StatusUnauthorized)
			return
		}
		mux.ServeHTTP(writer, request)
	})
}

func manifest(writer http.ResponseWriter, request *http.Request) {
	_, checksumValue, err := archiveWithChecksum()
	if err != nil {
		http.Error(writer, err.Error(), http.StatusInternalServerError)
		return
	}

	document := map[string]any{
		"schemaVersion": 1,
		"productID":     "com.eriyc.updater-example",
		"releases": []any{
			map[string]any{
				"id":            "0.2.0",
				"version":       "0.2.0",
				"channel":       "stable",
				"publishedAt":   "2026-03-01T03:10:56Z",
				"notesMarkdown": "Adds a staged resource update and relaunch flow.",
				"artifacts": []any{
					map[string]any{
						"os":     runtime.GOOS,
						"arch":   runtime.GOARCH,
						"kind":   updates.ArtifactKindBundleArchive,
						"format": updates.ArtifactFormatZip,
						"url":    baseURL(request) + "/artifacts/updater-example_0.2.0.zip",
						"sha256": checksumValue,
					},
				},
			},
		},
	}
	writer.Header().Set("Content-Type", "application/json")
	_ = json.NewEncoder(writer).Encode(document)
}

func artifact(writer http.ResponseWriter, _ *http.Request) {
	archive, err := buildArchive()
	if err != nil {
		http.Error(writer, err.Error(), http.StatusInternalServerError)
		return
	}
	writer.Header().Set("Content-Type", "application/zip")
	_, _ = writer.Write(archive)
}

func checksum(writer http.ResponseWriter, _ *http.Request) {
	_, checksumValue, err := archiveWithChecksum()
	if err != nil {
		http.Error(writer, err.Error(), http.StatusInternalServerError)
		return
	}
	_, _ = writer.Write([]byte(checksumValue + " updater-example_0.2.0.zip"))
}

func buildArchive() ([]byte, error) {
	buffer := bytes.NewBuffer(nil)
	archive := zip.NewWriter(buffer)

	bundleFile, err := archive.Create("bundle.json")
	if err != nil {
		return nil, err
	}
	bundle := updates.BundleManifest{
		SchemaVersion: 1,
		EntryPoint:    entryPoint(),
		Files: []updates.BundleFile{
			{Path: resourcePath(), Mode: "0644"},
		},
	}
	if err := json.NewEncoder(bundleFile).Encode(bundle); err != nil {
		return nil, err
	}

	resourceFile, err := archive.Create(resourcePath())
	if err != nil {
		return nil, err
	}
	if _, err := resourceFile.Write([]byte(`{"version":"0.2.0","status":"downloaded"}`)); err != nil {
		return nil, err
	}

	if err := archive.Close(); err != nil {
		return nil, err
	}
	return buffer.Bytes(), nil
}

func archiveWithChecksum() ([]byte, string, error) {
	archive, err := buildArchive()
	if err != nil {
		return nil, "", err
	}
	sum := sha256.Sum256(archive)
	return archive, hex.EncodeToString(sum[:]), nil
}

func entryPoint() string {
	if runtime.GOOS == "darwin" {
		return "Contents/MacOS/updater-example"
	}
	if runtime.GOOS == "windows" {
		return "updater-example.exe"
	}
	return "updater-example"
}

func resourcePath() string {
	if runtime.GOOS == "darwin" {
		return "Contents/Resources/update.json"
	}
	return "resources/update.json"
}

func baseURL(request *http.Request) string {
	scheme := "http"
	if request.TLS != nil {
		scheme = "https"
	}
	return scheme + "://" + request.Host
}
