"""Abstract job-board integration interface.

Every provider (LinkedIn, Naukri, Career Portal, …) implements this contract.
Business logic (views/services) only ever talks to this interface — adding a
new job board means writing one new subclass and registering it in factory.py.
No existing code changes.
"""

from abc import ABC, abstractmethod
from dataclasses import dataclass


class IntegrationError(Exception):
    """Raised by a provider when posting/updating/deleting fails."""


@dataclass
class PostResult:
    """What a successful post returns — stored on the JobPosting row."""
    external_post_id: str
    external_url: str


class JobBoardIntegration(ABC):
    """Contract every job-board provider must fulfil."""

    #: Machine name — must match a JobPosting.Channel value.
    channel: str = ""

    @abstractmethod
    def post_job(self, job) -> PostResult:
        """Publish the JD on the board. Return PostResult or raise IntegrationError."""

    @abstractmethod
    def update_job(self, job) -> PostResult:
        """Update an already-published JD. Return PostResult or raise IntegrationError."""

    @abstractmethod
    def delete_job(self, job) -> None:
        """Remove the JD from the board. Raise IntegrationError on failure."""

    # ---- shared helpers -------------------------------------------------

    def build_payload(self, job) -> dict:
        """Common JD → job-board payload mapping used by providers."""
        return {
            "title": job.title,
            "description": job.work_details,
            "location": job.location,
            "department": job.department,
            "experience": job.experience_band,
            "salary": job.ctc_band,
            "skills": job.must_have_skills,
            "qualifications": job.qualifications,
        }

    def validate(self, job, required_fields: tuple[str, ...]) -> None:
        """Fail fast (IntegrationError) when the JD lacks fields this board requires."""
        missing = [f for f in required_fields if not getattr(job, f, "")]
        if missing:
            raise IntegrationError(
                f"{self.channel}: JD is missing required field(s): {', '.join(missing)}"
            )
