"""Core dataclasses and enums shared across the project."""

from __future__ import annotations
from dataclasses import dataclass
from datetime import datetime
from enum import Enum


class PullStatus(str, Enum):
    """Outcome of a safe pull attempt."""
    UPDATED = "updated"
    ALREADY_UP_TO_DATE = "already_up_to_date"
    SKIPPED_DIRTY = "skipped_dirty"
    SKIPPED_CONFLICT = "skipped_conflict"
    SKIPPED_AHEAD_ONLY = "skipped_ahead_only"
    ERROR = "error"


class EventType(str, Enum):
    """Type of workspace event detected during a refresh cycle."""
    PIPELINE_FAILED = "pipeline_failed"
    PIPELINE_FIXED = "pipeline_fixed"
    MR_NEW_COMMENT = "mr_new_comment"
    MR_APPROVED = "mr_approved"
    MR_MERGED = "mr_merged"
    MR_NEW_PUSH = "mr_new_push"
    VULN_NEW = "vuln_new"
    VULN_RESOLVED = "vuln_resolved"
    AUTH_EXPIRED = "auth_expired"
    BRANCH_STALE = "branch_stale"
    BRANCH_DIVERGED = "branch_diverged"
    ISSUE_OPENED = "issue_opened"
    ISSUE_CLOSED = "issue_closed"
    ISSUE_REOPENED = "issue_reopened"
    ISSUE_NEW_COMMENT = "issue_new_comment"
    ISSUE_ASSIGNED = "issue_assigned"
    REPORT_STALE_TOO_LONG = "report_stale_too_long"
    FETCH_PARTIAL_FAILURE = "fetch_partial_failure"


@dataclass
class Repo:
    """A discovered git repository in the workspace."""
    name: str  # directory name
    path: str  # absolute path
    remote_url: str  # origin URL
    forge_host: str | None  # e.g. "gitlab.com"
    forge_type: str | None  # "gitlab" | "github" | None
    has_forge: bool
    project_path: str | None  # e.g. "myunisoft/myu-pa/services/pa-services-gateway"
    default_branch: str | None = None  # e.g. "main", fetched from forge API

    def to_dict(self) -> dict:
        return {
            "name": self.name, "path": self.path, "remote_url": self.remote_url,
            "forge_host": self.forge_host, "forge_type": self.forge_type,
            "has_forge": self.has_forge, "project_path": self.project_path,
            "default_branch": self.default_branch,
        }


@dataclass
class GitStatus:
    """Git branch state: ahead/behind counts, dirty flag, stale detection."""
    branch: str | None
    ahead: int
    behind: int
    dirty: bool
    stale: bool = False  # upstream configured but remote branch deleted (e.g. after merge)
    remote_branches: list[str] | None = None  # branch names on the remote (after fetch --prune)


@dataclass
class MR:
    """A merge request (GitLab) or pull request (GitHub)."""
    id: int
    iid: int
    title: str
    author: str
    state: str
    source_branch: str
    target_branch: str
    web_url: str
    user_notes_count: int
    approved: bool
    created_at: str
    updated_at: str
    repo_name: str  # filled by caller, not from API
    sha: str | None = None  # diffHeadSha — tracks new pushes
    assignees: list[str] | None = None
    reviewers: list[str] | None = None

    def involves(self, username: str) -> bool:
        """Return True if the user is author, assignee, or reviewer."""
        if self.author == username:
            return True
        if self.assignees and username in self.assignees:
            return True
        if self.reviewers and username in self.reviewers:
            return True
        return False

    def to_dict(self) -> dict:
        return {
            "id": self.id, "iid": self.iid, "title": self.title,
            "author": self.author, "state": self.state,
            "source_branch": self.source_branch, "target_branch": self.target_branch,
            "web_url": self.web_url, "user_notes_count": self.user_notes_count,
            "approved": self.approved, "created_at": self.created_at,
            "updated_at": self.updated_at, "repo_name": self.repo_name, "sha": self.sha,
            "assignees": self.assignees or [], "reviewers": self.reviewers or [],
        }


@dataclass
class Issue:
    """A forge issue (GitLab or GitHub)."""
    id: int
    iid: int
    title: str
    author: str
    state: str  # "opened" | "closed"
    web_url: str
    user_notes_count: int
    created_at: str
    updated_at: str
    repo_name: str  # filled by caller, not from API
    labels: list[str] | None = None
    assignees: list[str] | None = None
    participants: list[str] | None = None
    confidential: bool = False

    def involves(self, username: str) -> bool:
        """Return True if the user is author, assignee, or participant."""
        if self.author == username:
            return True
        if self.assignees and username in self.assignees:
            return True
        if self.participants and username in self.participants:
            return True
        return False

    def to_dict(self) -> dict:
        return {
            "id": self.id, "iid": self.iid, "title": self.title,
            "author": self.author, "state": self.state,
            "web_url": self.web_url, "user_notes_count": self.user_notes_count,
            "created_at": self.created_at, "updated_at": self.updated_at,
            "repo_name": self.repo_name,
            "labels": self.labels or [], "assignees": self.assignees or [],
            "participants": self.participants or [],
            "confidential": self.confidential,
        }


@dataclass
class Pipeline:
    """A CI pipeline (GitLab) or workflow run (GitHub Actions)."""
    id: int | str
    status: str
    ref: str
    web_url: str
    created_at: str
    repo_name: str

    def to_dict(self) -> dict:
        return {
            "id": self.id, "status": self.status, "ref": self.ref,
            "web_url": self.web_url, "created_at": self.created_at,
            "repo_name": self.repo_name,
        }


@dataclass
class Job:
    """A single job within a CI pipeline."""
    id: int | str
    name: str
    status: str
    stage: str
    web_url: str

    def to_dict(self) -> dict:
        return {
            "id": self.id, "name": self.name, "status": self.status,
            "stage": self.stage, "web_url": self.web_url,
        }


@dataclass
class Finding:
    """A lightweight security finding from a pipeline scan."""
    name: str
    title: str
    severity: str
    solution: str | None = None


@dataclass
class Vulnerability:
    """A security vulnerability from forge scanners."""
    id: int | str  # int for GitLab, prefixed str for GitHub (e.g. "dependabot-123")
    name: str
    severity: str
    state: str
    source: str  # reportType: SAST, DEPENDENCY_SCANNING, etc.
    web_url: str
    repo_name: str
    mitigation_state: str | None = None  # OPEN, RESOLVED, etc.
    mitigation_details: str | None = None
    location: str | None = None  # "file.py:123"
    solution: str | None = None  # scanner-recommended fix
    branch_status: str | None = None  # None, "fixed", "outdated_branch", "outdated_default", "introduced"
    report_stale: bool = False  # on default branch, pipeline findings don't include this vuln
    report_stale_since: str | None = None  # ISO timestamp of first report_stale detection

    def to_dict(self) -> dict:
        return {
            "id": self.id, "name": self.name, "severity": self.severity,
            "state": self.state, "source": self.source, "web_url": self.web_url,
            "repo_name": self.repo_name, "mitigation_state": self.mitigation_state,
            "mitigation_details": self.mitigation_details,
            "location": self.location, "solution": self.solution,
            "branch_status": self.branch_status,
            "report_stale": self.report_stale,
            "report_stale_since": self.report_stale_since,
        }


@dataclass
class Event:
    """A workspace event generated by diffing cache state across refreshes."""
    id: int | None  # DB-assigned
    type: EventType
    repo_name: str
    summary: str
    detail: str
    created_at: datetime
    seen: bool = False

    def to_dict(self) -> dict:
        return {
            "id": self.id, "type": self.type.value, "repo_name": self.repo_name,
            "summary": self.summary, "detail": self.detail,
            "created_at": self.created_at.isoformat()
            if isinstance(self.created_at, datetime) else self.created_at,
            "seen": self.seen,
        }


@dataclass
class RepoResult:
    """Result of a git operation (fetch, pull) on a single repo."""
    repo: str
    status: str
    message: str | None = None
