package plugin

import (
	"encoding/json"
	"time"
)

// DeploymentIntent is the bridge between a Trigger (which decides "deploy
// this") and a Source (which knows how to deploy). Reference is the
// source-interpreted handle: an image tag for image sources, a git sha for
// compose/static sources, "" for manual.
type DeploymentIntent struct {
	Reason      string            // "registry-push" | "git-push" | "manual" | "cron" | "promote"
	Reference   string            // tag, sha, or "" — Source decides
	Metadata    map[string]string // extra context (branch name, actor, etc.)
	TriggeredAt time.Time
	TriggeredBy string // username, "system", "webhook:<delivery-id>"
}

// PublicFace describes one externally-routable face of a Workload. A
// Workload may have several (e.g. compose stack with web + admin services).
// The proxy provider is configured kind-agnostically from this shape.
type PublicFace struct {
	Subdomain     string // e.g. "myapp"; "" means root of Domain
	Domain        string // "" inherits from settings.domain
	TargetService string // for compose: which service receives traffic; "" = single-container default
	TargetPort    int    // 0 = use container's primary exposed port
	AccessListID  int    // NPM access list, 0 = inherit
	EnableSSL     bool
}

// InboundEvent is what an upstream signal (webhook, poll, manual click,
// scheduler tick) looks like to a Trigger.Match call. Triggers consult
// Kind first to decide whether the event is interesting, then read the
// matching payload field. RawBody / Headers are kept so trigger plugins
// can perform their own signature verification or vendor-specific parsing.
type InboundEvent struct {
	Kind     string // "image-push" | "git-push" | "git-tag" | "manual" | "schedule"
	Image    *ImagePushEvent
	Git      *GitEvent
	Manual   *ManualEvent
	Schedule *ScheduleEvent
	RawBody  []byte
	Headers  map[string][]string
}

// ImagePushEvent is normalized across registry vendors (generic, Gitea,
// Docker Hub, ghcr, ...). Vendor-specific quirks are resolved by the
// webhook ingress before construction.
type ImagePushEvent struct {
	Registry string // hostname; "" for default registry
	Repo     string // owner/name
	Tag      string
	Digest   string // optional
}

// GitEvent covers both push (commits) and tag-create flavors. Vendor is
// "gitea" | "github" | "gitlab" | "" (autodetected).
//
// Deleted is true when the push event reports a branch / tag was deleted.
// Used by the preview-deploy flow to tear down ephemeral per-branch
// workloads when a feature branch is removed upstream. Inferred from
// GitHub-style `deleted: true` and Gitea's identical convention; GitLab
// signals deletion via after-SHA zeros (parsed at vendor level).
type GitEvent struct {
	Vendor    string
	Repo      string // owner/name
	Ref       string // refs/heads/main or refs/tags/v1.2.3
	Branch    string // populated for branch refs
	Tag       string // populated for tag refs
	CommitSHA string
	Pusher    string
	Deleted   bool
}

// ManualEvent represents a user-initiated deploy from the UI or API.
type ManualEvent struct {
	Actor     string
	Reference string // optional override (force a specific tag / sha / revision)
	Note      string
}

// ScheduleEvent is fired by the internal scheduler when a schedule
// trigger's next-fire window is reached. FiredAt is the wall-clock the
// scheduler observed (already truncated to the second). The trigger
// plugin uses FiredAt + its own config to populate the DeploymentIntent.
type ScheduleEvent struct {
	FiredAt time.Time
}

// SourceConfigOf decodes the workload's SourceConfig blob into the typed
// shape a specific Source uses. Kept here so callers do not duplicate the
// boilerplate.
func SourceConfigOf[T any](w Workload) (T, error) {
	var out T
	if len(w.SourceConfig) == 0 {
		return out, nil
	}
	err := json.Unmarshal(w.SourceConfig, &out)
	return out, err
}

// TriggerConfigOf is the symmetric helper for TriggerConfig.
func TriggerConfigOf[T any](w Workload) (T, error) {
	var out T
	if len(w.TriggerConfig) == 0 {
		return out, nil
	}
	err := json.Unmarshal(w.TriggerConfig, &out)
	return out, err
}
