"""Campaign and CampaignTest models.

Campaigns group multiple tests into a kill chain sequence,
enabling simulation of complete attack chains and APT emulations.
"""

# Import uuid
import uuid

# Import  from sqlalchemy
from sqlalchemy import (
    Boolean,
    Column,
    DateTime,
    ForeignKey,
    Index,
    Integer,
    String,
    Text,
    func,
)

# Import JSONB, UUID from sqlalchemy.dialects.postgresql
from sqlalchemy.dialects.postgresql import JSONB, UUID

# Import relationship from sqlalchemy.orm
from sqlalchemy.orm import relationship

# Import Base from app.database
from app.database import Base


# Define class Campaign
class Campaign(Base):
    """A campaign groups multiple tests into a sequenced attack chain.

    Types:
    - custom: manually created campaign
    - apt_emulation: generated from a threat actor profile
    - kill_chain: structured around kill chain phases
    - compliance: targeting specific compliance requirements

    Status:
    - draft: being configured
    - active: tests are being executed
    - completed: all tests done
    - archived: historical record
    """
    # Assign __tablename__ = "campaigns"
    __tablename__ = "campaigns"

    # Assign id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
    id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
    # Assign name = Column(String, nullable=False)
    name = Column(String, nullable=False)
    # Assign description = Column(Text, nullable=True)
    description = Column(Text, nullable=True)
    # Assign type = Column(String, nullable=False, default="custom")       # custom, ap...
    type = Column(String, nullable=False, default="custom")       # custom, apt_emulation, kill_chain, compliance
    # Assign threat_actor_id = Column(
    threat_actor_id = Column(
        UUID(as_uuid=True),
        ForeignKey("threat_actors.id", ondelete="SET NULL"),
        # Keyword argument: nullable
        nullable=True,
    )
    # Assign status = Column(String, nullable=False, default="draft")      # draft, activ...
    status = Column(String, nullable=False, default="draft")      # draft, active, completed, archived
    # Assign created_by = Column(
    created_by = Column(
        UUID(as_uuid=True),
        ForeignKey("users.id", ondelete="SET NULL"),
        # Keyword argument: nullable
        nullable=True,
    )
    start_date = Column(DateTime, nullable=True)   # campaign won't activate before this date
    scheduled_at = Column(DateTime, nullable=True)
    approved_by = Column(
        UUID(as_uuid=True),
        ForeignKey("users.id", ondelete="SET NULL"),
        nullable=True,
    )
    approved_at = Column(DateTime, nullable=True)
    rejection_reason = Column(Text, nullable=True)
    # Assign completed_at = Column(DateTime, nullable=True)
    completed_at = Column(DateTime, nullable=True)
    # Assign target_platform = Column(String, nullable=True)
    target_platform = Column(String, nullable=True)
    # Assign tags = Column(JSONB, nullable=True, default=[])
    tags = Column(JSONB, nullable=True, default=[])
    # Assign created_at = Column(DateTime(timezone=True), server_default=func.now())
    created_at = Column(DateTime(timezone=True), server_default=func.now())
    # Assign data_classification = Column(String(20), nullable=False, server_default="internal")
    data_classification = Column(String(20), nullable=False, server_default="confidential")

    # Recurring scheduling fields
    is_recurring = Column(Boolean, default=False)
    # Assign recurrence_pattern = Column(String, nullable=True)   # weekly, monthly, quarterly
    recurrence_pattern = Column(String, nullable=True)   # weekly, monthly, quarterly
    # Assign next_run_at = Column(DateTime, nullable=True)
    next_run_at = Column(DateTime, nullable=True)
    # Assign last_run_at = Column(DateTime, nullable=True)
    last_run_at = Column(DateTime, nullable=True)
    # Assign parent_campaign_id = Column(
    parent_campaign_id = Column(
        UUID(as_uuid=True),
        ForeignKey("campaigns.id", ondelete="SET NULL"),
        # Keyword argument: nullable
        nullable=True,
    )

    # Relationships
    threat_actor = relationship("ThreatActor")
    # Assign creator = relationship("User", foreign_keys=[created_by])
    creator = relationship("User", foreign_keys=[created_by])
    # Assign campaign_tests = relationship(
    campaign_tests = relationship(
        # Literal argument value
        "CampaignTest",
        # Keyword argument: back_populates
        back_populates="campaign",
        # Keyword argument: cascade
        cascade="all, delete-orphan",
        # Keyword argument: order_by
        order_by="CampaignTest.order_index",
    )
    # Assign parent_campaign = relationship(
    parent_campaign = relationship(
        # Literal argument value
        "Campaign",
        # Keyword argument: remote_side
        remote_side="Campaign.id",
        # Keyword argument: foreign_keys
        foreign_keys=[parent_campaign_id],
    )
    # Assign child_campaigns = relationship(
    child_campaigns = relationship(
        # Literal argument value
        "Campaign",
        # Keyword argument: foreign_keys
        foreign_keys=[parent_campaign_id],
        # Keyword argument: back_populates
        back_populates="parent_campaign",
    )

    # Assign __table_args__ = (
    __table_args__ = (
        Index('ix_campaigns_status', 'status'),
        Index('ix_campaigns_type', 'type'),
        Index('ix_campaigns_threat_actor', 'threat_actor_id'),
        Index('ix_campaigns_created_by', 'created_by'),
        Index('ix_campaigns_next_run', 'next_run_at'),
    )


# Kill chain phases in order (for sorting and validation)
KILL_CHAIN_PHASES = [
    # Literal argument value
    "reconnaissance",
    # Literal argument value
    "resource_development",
    # Literal argument value
    "initial_access",
    # Literal argument value
    "execution",
    # Literal argument value
    "persistence",
    # Literal argument value
    "privilege_escalation",
    # Literal argument value
    "defense_evasion",
    # Literal argument value
    "credential_access",
    # Literal argument value
    "discovery",
    # Literal argument value
    "lateral_movement",
    # Literal argument value
    "collection",
    # Literal argument value
    "command_and_control",
    # Literal argument value
    "exfiltration",
    # Literal argument value
    "impact",
]


# Define class CampaignTest
class CampaignTest(Base):
    """A test within a campaign, with ordering and dependency information.

    ``depends_on`` creates a self-referential chain (A -> B -> C).
    Circular dependencies are validated at the service layer.
    """
    # Assign __tablename__ = "campaign_tests"
    __tablename__ = "campaign_tests"

    # Assign id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
    id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
    # Assign campaign_id = Column(
    campaign_id = Column(
        UUID(as_uuid=True),
        ForeignKey("campaigns.id", ondelete="CASCADE"),
        # Keyword argument: nullable
        nullable=False,
    )
    # Assign test_id = Column(
    test_id = Column(
        UUID(as_uuid=True),
        ForeignKey("tests.id", ondelete="CASCADE"),
        # Keyword argument: nullable
        nullable=False,
    )
    # Assign order_index = Column(Integer, nullable=False, default=0)
    order_index = Column(Integer, nullable=False, default=0)
    # Assign depends_on = Column(
    depends_on = Column(
        UUID(as_uuid=True),
        ForeignKey("campaign_tests.id", ondelete="SET NULL"),
        # Keyword argument: nullable
        nullable=True,
    )
    # Assign phase = Column(String, nullable=True)   # kill chain phase
    phase = Column(String, nullable=True)   # kill chain phase

    # Relationships
    campaign = relationship("Campaign", back_populates="campaign_tests")
    # Assign test = relationship("Test")
    test = relationship("Test")
    # Assign dependency = relationship("CampaignTest", remote_side="CampaignTest.id")
    dependency = relationship("CampaignTest", remote_side="CampaignTest.id")

    # Assign __table_args__ = (
    __table_args__ = (
        Index('ix_campaign_tests_campaign', 'campaign_id'),
        Index('ix_campaign_tests_test', 'test_id'),
    )


class CampaignModificationRequest(Base):
    """A lead's request to add or remove a test from an already-active campaign.

    The underlying ``CampaignTest`` row is only created/deleted once a
    manager approves the request — see ``approve_modification_request``
    in ``campaign_crud_service.py``.
    """
    __tablename__ = "campaign_modification_requests"

    id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
    campaign_id = Column(
        UUID(as_uuid=True),
        ForeignKey("campaigns.id", ondelete="CASCADE"),
        nullable=False,
    )
    requested_by = Column(
        UUID(as_uuid=True),
        ForeignKey("users.id", ondelete="SET NULL"),
        nullable=True,
    )
    action = Column(String, nullable=False)   # add_test, remove_test
    # SET NULL (not CASCADE): approving a "remove_test" request deletes the
    # underlying Test row (see remove_test_from_campaign), and this request
    # row is the audit record of that decision — it must survive the test's
    # deletion, not be wiped out along with it.
    test_id = Column(
        UUID(as_uuid=True),
        ForeignKey("tests.id", ondelete="SET NULL"),
        nullable=True,
    )
    order_index = Column(Integer, nullable=True)
    phase = Column(String, nullable=True)
    justification = Column(Text, nullable=False)
    status = Column(String, nullable=False, default="pending")   # pending, approved, rejected
    reviewed_by = Column(
        UUID(as_uuid=True),
        ForeignKey("users.id", ondelete="SET NULL"),
        nullable=True,
    )
    reviewed_at = Column(DateTime, nullable=True)
    review_notes = Column(Text, nullable=True)
    created_at = Column(DateTime(timezone=True), server_default=func.now())

    campaign = relationship("Campaign")
    test = relationship("Test")
    requester = relationship("User", foreign_keys=[requested_by])
    reviewer = relationship("User", foreign_keys=[reviewed_by])

    __table_args__ = (
        Index("ix_campaign_mod_requests_campaign", "campaign_id"),
        Index("ix_campaign_mod_requests_status", "status"),
    )
