"""
CHOIR v2 Coordination Receipts

Receipt types for multi-agent coordination with lease-based claiming,
joining, micro-changes, and suspend/resume patterns.

Schema: receipt.choir_v2.{hunk_claim|join|micro_change|suspend|resume|merge_intent|help_signal}.v1
"""
from __future__ import annotations

from datetime import datetime, timedelta, timezone
from typing import List, Optional

from pydantic import Field

from receipts.base import BaseReceipt


class HunkClaimReceipt(BaseReceipt):
    """
    Claim specific line range (hunk) in a file for editing.

    Non-exclusive: Multiple agents can claim overlapping hunks if coordinated.
    Mints a lease with TTL - agent must heartbeat or complete within TTL.

    Schema: receipt.choir_v2.hunk_claim.v1
    """
    schema: str = Field(default="receipt.choir_v2.hunk_claim.v1")
    task_id: str = ""
    path: str = ""
    lines: List[int] = Field(default_factory=lambda: [1, 2_147_483_647])  # [start, end]
    ttl_s: int = 5400  # 90 minutes default
    state: str = "claimed"  # claimed | active | completed | expired
    agent: str = ""

    def expires_at(self) -> datetime:
        """Calculate lease expiration time."""
        return self.ts + timedelta(seconds=self.ttl_s)

    def is_expired(self) -> bool:
        """Check if lease has expired."""
        return datetime.now(timezone.utc) > self.expires_at()


class JoinReceipt(BaseReceipt):
    """
    Join existing work without claiming ownership.

    Allows collaboration on a task claimed by another agent.
    Role: assist (default) | review | observe

    Schema: receipt.choir_v2.join.v1
    """
    schema: str = Field(default="receipt.choir_v2.join.v1")
    task_id: str = ""
    claim_id: str = ""  # Reference to HunkClaimReceipt being joined
    path: str = ""
    role: str = "assist"  # assist | review | observe
    ttl_s: int = 3600  # 1 hour default (shorter than claims)
    agent: str = ""


class MicroChangeReceipt(BaseReceipt):
    """
    Fast path for small changes (<20 lines).

    Bypasses full claim workflow for minor edits.
    No lease, no TTL - just mint receipt and commit.

    Schema: receipt.choir_v2.micro_change.v1
    """
    schema: str = Field(default="receipt.choir_v2.micro_change.v1")
    paths: List[str] = Field(default_factory=list)  # Can touch multiple files
    why: str = ""  # Brief reason for change
    diff_stats: dict = Field(default_factory=dict)  # {insertions: N, deletions: M}
    agent: str = ""

    def total_lines_changed(self) -> int:
        """Total lines changed (insertions + deletions)."""
        return self.diff_stats.get("insertions", 0) + self.diff_stats.get("deletions", 0)

    def is_valid_micro(self) -> bool:
        """Check if change qualifies as micro (<20 lines)."""
        return self.total_lines_changed() < 20


class MergeIntentReceipt(BaseReceipt):
    """
    Declare intent to merge changes in shared file.

    Used for files with comment-delimited segments where multiple
    agents work concurrently. Declares boundaries to prevent conflicts.

    Schema: receipt.choir_v2.merge_intent.v1
    """
    schema: str = Field(default="receipt.choir_v2.merge_intent.v1")
    task_id: str = ""
    path: str = ""
    start_marker: str = ""  # e.g., "<!-- AGENT:copilot START -->"
    end_marker: str = ""    # e.g., "<!-- AGENT:copilot END -->"
    segment_hash: str = ""  # Hash of segment content at intent time
    agent: str = ""


class SuspendReceipt(BaseReceipt):
    """
    Pause work on a claim (async-friendly).

    Releases lease temporarily, allowing handoff or resumption later.
    Claim can be resumed by same or different agent.

    Schema: receipt.choir_v2.suspend.v1
    """
    schema: str = Field(default="receipt.choir_v2.suspend.v1")
    claim_id: str = ""  # Reference to HunkClaimReceipt being suspended
    reason: str = ""    # Why suspended (e.g., "blocked", "waiting_review")
    agent: str = ""


class ResumeReceipt(BaseReceipt):
    """
    Resume suspended work.

    Re-activates a suspended claim, minting a new lease.
    Can be issued by original agent or another agent (handoff).

    Schema: receipt.choir_v2.resume.v1
    """
    schema: str = Field(default="receipt.choir_v2.resume.v1")
    claim_id: str = ""      # Reference to original HunkClaimReceipt
    suspend_id: str = ""    # Reference to SuspendReceipt
    ttl_s: int = 5400       # New lease TTL
    agent: str = ""

    def expires_at(self) -> datetime:
        """Calculate new lease expiration time."""
        return self.ts + timedelta(seconds=self.ttl_s)


class HelpSignalReceipt(BaseReceipt):
    """
    Request help from another agent.

    Broadcasts need for specific capabilities (e.g., testing, review, domain expertise).
    Severity: info | warn | blocker

    Schema: receipt.choir_v2.help_signal.v1
    """
    schema: str = Field(default="receipt.choir_v2.help_signal.v1")
    task_id: str = ""
    claim_id: Optional[str] = None  # Optional reference to claim needing help
    severity: str = "warn"  # info | warn | blocker
    capabilities: List[str] = Field(default_factory=list)  # Required skills
    message: str = ""  # Human-readable help request
    agent: str = ""

    def is_blocker(self) -> bool:
        """Check if help signal is a blocker (4h SLO)."""
        return self.severity == "blocker"


# Export all receipt types
__all__ = [
    "HunkClaimReceipt",
    "JoinReceipt",
    "MicroChangeReceipt",
    "MergeIntentReceipt",
    "SuspendReceipt",
    "ResumeReceipt",
    "HelpSignalReceipt",
]
