package gitcode

import (
	"fmt"
	"time"
)

type ErrNetworkUnavailable struct {
	Endpoint string
	Status   int
	Attempts int
	Cause    error
	Recovery string
}

func (e ErrNetworkUnavailable) Error() string {
	if e.Recovery == "" {
		e.Recovery = "retry with a longer timeout or check connectivity"
	}
	return fmt.Sprintf("gitcode: network unavailable for %s after %d attempt(s): %s", e.Endpoint, e.Attempts, e.Recovery)
}

func (e ErrNetworkUnavailable) Unwrap() error { return e.Cause }

func (e ErrNetworkUnavailable) DiagnosticCode() string { return "network_unavailable" }

type ErrRateLimited struct {
	RetryAfter    time.Duration
	RawRetryAfter string
	Endpoint      string
	Attempts      int
}

func (e ErrRateLimited) Error() string {
	return fmt.Sprintf("gitcode: rate limited for %s after %d attempt(s): retry after %s", e.Endpoint, e.Attempts, e.RetryAfter)
}

func (e ErrRateLimited) DiagnosticCode() string { return "rate_limited" }

type ErrAuthExpired struct {
	Endpoint string
	Status   int
	Message  string
}

func (e ErrAuthExpired) Error() string {
	if e.Message == "" {
		e.Message = "authentication expired; renew your token and try again"
	}
	return fmt.Sprintf("gitcode: auth expired for %s: %s", e.Endpoint, e.Message)
}

func (e ErrAuthExpired) DiagnosticCode() string { return "auth_expired" }

type ErrForbidden struct {
	Endpoint string
	Status   int
	Message  string
	Recovery string
}

func (e ErrForbidden) Error() string {
	if e.Recovery == "" {
		e.Recovery = "check GitCode permissions for this resource"
	}
	if e.Message == "" {
		e.Message = "forbidden"
	}
	return fmt.Sprintf("gitcode: forbidden for %s: %s; %s", e.Endpoint, e.Message, e.Recovery)
}

func (e ErrForbidden) DiagnosticCode() string { return "forbidden" }

type ErrNotFound struct {
	Endpoint string
	ID       string
	Message  string
}

func (e ErrNotFound) Error() string {
	if e.Message == "" {
		e.Message = "not found"
	}
	return fmt.Sprintf("gitcode: %s at %s", e.Message, e.Endpoint)
}

func (e ErrNotFound) DiagnosticCode() string { return "not_found" }

type ErrConflict struct {
	Endpoint      string
	Status        int
	LocalPayload  []byte
	RemotePayload []byte
	Message       string
}

func (e ErrConflict) Error() string {
	if e.Message == "" {
		e.Message = "remote conflict"
	}
	return fmt.Sprintf("gitcode: conflict for %s: %s", e.Endpoint, e.Message)
}

func (e ErrConflict) DiagnosticCode() string { return "remote_conflict" }

type ErrPartialResponse struct {
	Endpoint    string
	Expected    int64
	Got         int64
	Cause       error
	Message     string
	ContentType string
	Offset      int64
	Attempts    int
}

func (e ErrPartialResponse) Error() string {
	if e.Message != "" {
		return fmt.Sprintf("gitcode: partial response for %s: %s", e.Endpoint, e.Message)
	}
	if e.Expected > 0 || e.Got > 0 {
		return fmt.Sprintf("gitcode: partial response for %s: expected %d bytes, got %d bytes", e.Endpoint, e.Expected, e.Got)
	}
	return fmt.Sprintf("gitcode: partial response for %s", e.Endpoint)
}

func (e ErrPartialResponse) Unwrap() error { return e.Cause }

func (e ErrPartialResponse) DiagnosticCode() string { return "partial_response" }

type ErrRemoteCollision struct {
	Alias      string
	ExistingID string
	NewID      string
	Endpoint   string
}

func (e ErrRemoteCollision) Error() string {
	return fmt.Sprintf("gitcode: remote id %s already maps to %s; cannot map to %s", e.Alias, e.ExistingID, e.NewID)
}

func (e ErrRemoteCollision) DiagnosticCode() string { return "remote_collision" }

type ErrRemoteNotFound struct {
	Endpoint string
	Alias    string
	Message  string
}

func (e ErrRemoteNotFound) Error() string {
	if e.Message == "" {
		e.Message = "remote record not found"
	}
	return fmt.Sprintf("gitcode: %s for alias %s at %s", e.Message, e.Alias, e.Endpoint)
}

func (e ErrRemoteNotFound) DiagnosticCode() string { return "remote_not_found" }

type ErrAPIValidation struct {
	Endpoint string
	Status   int
	Message  string
}

func (e ErrAPIValidation) Error() string {
	if e.Message == "" {
		e.Message = "api validation failed"
	}
	return fmt.Sprintf("gitcode: api validation failed for %s: %s", e.Endpoint, e.Message)
}

func (e ErrAPIValidation) DiagnosticCode() string { return "api_validation" }

type ErrPayloadTooLarge struct {
	Endpoint string
	Limit    int64
	Size     int64
	Source   string
}

func (e ErrPayloadTooLarge) Error() string {
	return fmt.Sprintf("gitcode: response for %s exceeds maximum size %d bytes", e.Endpoint, e.Limit)
}

func (e ErrPayloadTooLarge) DiagnosticCode() string { return "payload_too_large" }

func (e ErrPayloadTooLarge) FailureSource() string { return e.Source }

type ErrEmptyWiki struct {
	Owner string
	Repo  string
}

func (e ErrEmptyWiki) Error() string {
	return fmt.Sprintf("gitcode: wiki is empty or uninitialized for %s/%s; run `gitcode-mcp wiki init --repo %s/%s` or create a page via the GitCode UI", e.Owner, e.Repo, e.Owner, e.Repo)
}

func (e ErrEmptyWiki) DiagnosticCode() string { return "empty_wiki" }

type ErrWriteConfirmationIncomplete struct {
	Endpoint string
	Message  string
	RemoteID string
	Cause    error
}

// ErrWriteMutationPhase preserves whether the mutating request crossed the
// transport boundary. Readback failures and most transport failures are
// ambiguous and must not be retried as fresh mutations.
type ErrWriteMutationPhase struct {
	Endpoint          string
	Phase             string
	MutationAttempted bool
	Cause             error
}

func (e ErrWriteMutationPhase) Error() string {
	return fmt.Sprintf("gitcode: write mutation %s failed for %s: %v", e.Phase, e.Endpoint, e.Cause)
}

func (e ErrWriteMutationPhase) Unwrap() error { return e.Cause }

func (e ErrWriteMutationPhase) DiagnosticCode() string { return "write_mutation_phase" }

// ErrWritePreconditionConflict reports a changed canonical preimage without
// exposing its contents in diagnostics.
type ErrWritePreconditionConflict struct {
	Endpoint            string
	ExpectedFingerprint string
	ActualFingerprint   string
}

func (e ErrWritePreconditionConflict) Error() string {
	return fmt.Sprintf("gitcode: write preimage changed before mutation for %s", e.Endpoint)
}

func (e ErrWritePreconditionConflict) DiagnosticCode() string {
	return "write_precondition_conflict"
}

type ErrDiscussionReplyUnavailable struct {
	DiscussionID    string
	ParentCommentID string
	Message         string
}

func (e ErrDiscussionReplyUnavailable) Error() string {
	message := e.Message
	if message == "" {
		message = "no provider discussion id was proven by live readback"
	}
	return fmt.Sprintf("gitcode: discussion reply unavailable for %s (parent %s): %s", e.DiscussionID, e.ParentCommentID, message)
}

func (e ErrDiscussionReplyUnavailable) DiagnosticCode() string {
	return "discussion_reply_unavailable"
}

type ErrPRReviewAnchorMismatch struct {
	Endpoint     string
	CommentID    string
	ExpectedPath string
	ActualPath   string
	ExpectedLine int
	ActualLine   int
}

func (e ErrPRReviewAnchorMismatch) RemoteWriteID() string { return e.CommentID }

func (e ErrWriteConfirmationIncomplete) RemoteWriteID() string { return e.RemoteID }

func (e ErrPRReviewAnchorMismatch) Error() string {
	return fmt.Sprintf("gitcode: PR review anchor mismatch for %s: requested %s:%d, read back %s:%d", e.Endpoint, e.ExpectedPath, e.ExpectedLine, e.ActualPath, e.ActualLine)
}

func (e ErrPRReviewAnchorMismatch) DiagnosticCode() string {
	return "pr_review_anchor_mismatch"
}

type ErrPushMirrorSyncInProgress struct {
	Endpoint string
	Message  string
}

func (e ErrPushMirrorSyncInProgress) Error() string {
	if e.Message == "" {
		e.Message = "push mirror synchronization is already running or cooling down"
	}
	return fmt.Sprintf("gitcode: push mirror sync unavailable for %s: %s", e.Endpoint, e.Message)
}

func (e ErrPushMirrorSyncInProgress) DiagnosticCode() string {
	return "push_mirror_sync_in_progress"
}

func (e ErrWriteConfirmationIncomplete) Error() string {
	if e.Message == "" {
		e.Message = "write confirmation incomplete"
	}
	return fmt.Sprintf("gitcode: write confirmation incomplete for %s: %s", e.Endpoint, e.Message)
}

func (e ErrWriteConfirmationIncomplete) Unwrap() error { return e.Cause }

func (e ErrWriteConfirmationIncomplete) DiagnosticCode() string {
	return "write_confirmation_incomplete"
}