package provider

import (
	"context"
	"strings"
	"time"

	"git.toki-labs.com/toki/gito/services/core/internal/core"
)

type ProviderID string

const (
	ProviderGitHub  ProviderID = "github"
	ProviderGitLab  ProviderID = "gitlab"
	ProviderGitea   ProviderID = "gitea"
	ProviderForgejo ProviderID = "forgejo"
	ProviderPlane   ProviderID = "plane"
	ProviderJira    ProviderID = "jira"
)

type Capability uint64

const (
	CapabilityChangeRequest Capability = 1 << iota
	CapabilityWebhook
	CapabilityChecks
	CapabilityComment
)

func (c Capability) Has(flag Capability) bool {
	return flag != 0 && c&flag == flag
}

type Config struct {
	ID            ProviderID
	Endpoint      string
	CredentialRef string
	Capabilities  Capability
	CreatedAt     time.Time
	UpdatedAt     time.Time
}

func (c Config) Supports(flag Capability) bool {
	return c.Capabilities.Has(flag)
}

func (c Config) Normalized() Config {
	c.ID = ProviderID(strings.ToLower(strings.TrimSpace(string(c.ID))))
	c.Endpoint = strings.TrimSpace(c.Endpoint)
	c.CredentialRef = strings.TrimSpace(c.CredentialRef)
	return c
}

type ChangeRequestAdapter interface {
	CreateChangeRequest(ctx context.Context, input core.ChangeRequestCreate) (core.ChangeRequest, error)
	UpdateChangeRequest(ctx context.Context, id string, input core.ChangeRequestUpdate) (core.ChangeRequest, error)
	CommentChangeRequest(ctx context.Context, id string, input core.ChangeRequestComment) error
	RequestChangeRequestReview(ctx context.Context, id string, input core.ChangeRequestReviewRequest) error
	MergeChangeRequest(ctx context.Context, id string, input core.ChangeRequestMerge) (core.ChangeRequest, error)
	CloseChangeRequest(ctx context.Context, id string, input core.ChangeRequestClose) (core.ChangeRequest, error)
}

type ChangeRequestMappingCandidate struct {
	Provider        ProviderID
	ProviderObject  string
	Fields          []ChangeRequestFieldMapping
	States          []ChangeRequestStateMapping
	Actions         []core.ChangeRequestAction
	ExtensionFields []ChangeRequestExtensionField
}

type ChangeRequestFieldMapping struct {
	Target string
	Source string
	Notes  string
}

type ChangeRequestStateMapping struct {
	State  core.ChangeRequestState
	Source string
	Notes  string
}

// ChangeRequestExtensionField describes provider-owned metadata that adapters
// keep outside core.ChangeRequest until a later milestone promotes it.
type ChangeRequestExtensionField struct {
	Name   string
	Source string
	Notes  string
}

func ChangeRequestMappingCandidates() []ChangeRequestMappingCandidate {
	return []ChangeRequestMappingCandidate{
		GitHubChangeRequestMapping(),
		GitLabChangeRequestMapping(),
		GiteaChangeRequestMapping(),
	}
}

func GitHubChangeRequestMapping() ChangeRequestMappingCandidate {
	return ChangeRequestMappingCandidate{
		Provider:       ProviderGitHub,
		ProviderObject: "pull_request",
		Fields: []ChangeRequestFieldMapping{
			{Target: "provider", Source: string(ProviderGitHub)},
			{Target: "external_id", Source: "number/pull_number"},
			{Target: "repo_id", Source: "Gito repo binding"},
			{Target: "source_branch", Source: "head"},
			{Target: "target_branch", Source: "base"},
			{Target: "title", Source: "title"},
			{Target: "body", Source: "body"},
			{Target: "draft", Source: "draft"},
			{Target: "state", Source: "state + merged check"},
		},
		States: []ChangeRequestStateMapping{
			{State: core.ChangeRequestOpen, Source: "state=open"},
			{State: core.ChangeRequestMerged, Source: "merged=true or merge endpoint success"},
			{State: core.ChangeRequestClosed, Source: "state=closed and merged=false"},
		},
		Actions: changeRequestActions(),
		ExtensionFields: []ChangeRequestExtensionField{
			{Name: "head_repo", Source: "head_repo", Notes: "cross-repository create metadata"},
			{Name: "maintainer_can_modify", Source: "maintainer_can_modify"},
			{Name: "merge_method", Source: "merge_method"},
			{Name: "issue", Source: "issue", Notes: "issue conversion and issue-only fields stay adapter-owned"},
		},
	}
}

func GitLabChangeRequestMapping() ChangeRequestMappingCandidate {
	return ChangeRequestMappingCandidate{
		Provider:       ProviderGitLab,
		ProviderObject: "merge_request",
		Fields: []ChangeRequestFieldMapping{
			{Target: "provider", Source: string(ProviderGitLab)},
			{Target: "external_id", Source: "iid"},
			{Target: "repo_id", Source: "Gito repo binding"},
			{Target: "source_branch", Source: "source_branch"},
			{Target: "target_branch", Source: "target_branch"},
			{Target: "title", Source: "title"},
			{Target: "body", Source: "description"},
			{Target: "draft", Source: "draft"},
			{Target: "state", Source: "state"},
		},
		States: []ChangeRequestStateMapping{
			{State: core.ChangeRequestOpen, Source: "state=opened"},
			{State: core.ChangeRequestMerged, Source: "state=merged"},
			{State: core.ChangeRequestClosed, Source: "state=closed"},
		},
		Actions: changeRequestActions(),
		ExtensionFields: []ChangeRequestExtensionField{
			{Name: "project_id", Source: "project_id/source_project_id/target_project_id"},
			{Name: "detailed_merge_status", Source: "detailed_merge_status"},
			{Name: "squash_on_merge", Source: "squash_on_merge"},
			{Name: "merge_after", Source: "merge_after"},
			{Name: "blocking_discussions_resolved", Source: "blocking_discussions_resolved"},
		},
	}
}

func GiteaChangeRequestMapping() ChangeRequestMappingCandidate {
	return ChangeRequestMappingCandidate{
		Provider:       ProviderGitea,
		ProviderObject: "pull_request",
		Fields: []ChangeRequestFieldMapping{
			{Target: "provider", Source: string(ProviderGitea)},
			{Target: "external_id", Source: "number"},
			{Target: "repo_id", Source: "Gito repo binding"},
			{Target: "source_branch", Source: "head"},
			{Target: "target_branch", Source: "base"},
			{Target: "title", Source: "title"},
			{Target: "body", Source: "body"},
			{Target: "draft", Source: "draft"},
			{Target: "state", Source: "state + merged"},
		},
		States: []ChangeRequestStateMapping{
			{State: core.ChangeRequestOpen, Source: "state=open"},
			{State: core.ChangeRequestMerged, Source: "merged=true"},
			{State: core.ChangeRequestClosed, Source: "state=closed and merged=false"},
		},
		Actions: changeRequestActions(),
		ExtensionFields: []ChangeRequestExtensionField{
			{Name: "mergeable", Source: "mergeable"},
			{Name: "allow_maintainer_edit", Source: "allow_maintainer_edit"},
			{Name: "merge_style", Source: "merge option style"},
			{Name: "deadline", Source: "due_date"},
			{Name: "requested_reviewers", Source: "requested_reviewers/requested_reviewers_teams"},
		},
	}
}

func changeRequestActions() []core.ChangeRequestAction {
	return []core.ChangeRequestAction{
		core.ChangeRequestActionCreate,
		core.ChangeRequestActionUpdate,
		core.ChangeRequestActionComment,
		core.ChangeRequestActionRequestReview,
		core.ChangeRequestActionMerge,
		core.ChangeRequestActionClose,
	}
}

type WebhookEvent struct {
	Provider   string
	EventType  string
	ExternalID string
	Payload    []byte
}

// WebhookRequest represents a provider webhook with all metadata needed
// for signature verification and normalization.
type WebhookRequest struct {
	Provider   ProviderID
	EventType  string
	ExternalID string
	Payload    []byte
	Headers    map[string][]string
	Query      map[string][]string
	ReceivedAt time.Time
}

// WebhookCandidate represents a provider-neutral normalization candidate
// that contains minimal metadata without requiring provider secrets.
type WebhookCandidate struct {
	Type       string
	Provider   ProviderID
	ExternalID string
	Revision   *core.RevisionEvent
}

// WebhookVerifier defines the interface for verifying webhook signatures.
type WebhookVerifier interface {
	VerifyWebhook(ctx context.Context, req WebhookRequest) error
}

// WebhookNormalizer defines the interface for normalizing webhook events
// into provider-neutral candidates.
type WebhookNormalizer interface {
	NormalizeWebhook(ctx context.Context, req WebhookRequest) ([]WebhookCandidate, error)
}

// WebhookAdapter combines verification and normalization capabilities.
type WebhookAdapter interface {
	WebhookVerifier
	WebhookNormalizer
}
