from __future__ import annotations

import csv
import re
from dataclasses import dataclass
from pathlib import Path
from typing import Callable, List, Optional


ConfirmFn = Callable[[str], bool]


@dataclass
class Contact:
    name: str
    profile_url: str
    email: Optional[str] = None


class LinkedInEmployeeCollector:
    """
    Best-effort collector of employee profile links from a LinkedIn company page.

    Important:
    - LinkedIn's UI changes frequently and scraping may violate ToS.
    - This code aims to provide a starting point with authenticated browsing, but
      you should review results manually before contacting anyone.
    """

    def collect(
        self,
        *,
        page,
        company_url: str,
        max_contacts: int,
        confirm_before_start: ConfirmFn,
        scrape_emails: bool = False,
        screenshots_dir: Optional[Path] = None,
    ) -> List[Contact]:
        company_url = company_url.strip()
        if not company_url:
            return []

        ok = confirm_before_start(f"Start collecting employee profiles from:\n{company_url}\nContinue?")
        if not ok:
            return []

        page.goto(company_url, wait_until="domcontentloaded")

        # Try to click through to the employees listing page.
        btn = page.get_by_role(
            "link",
            name=re.compile(r"(see all employees|see all|employees)", re.I),
        ).first
        if btn and btn.count() > 0:
            try:
                btn.click()
            except Exception:
                pass

        # Collect visible profile links.
        # We intentionally avoid deep scrolling loops for safety/time; users can bump max/iterations later.
        anchors = page.locator('a[href*="/in/"]')
        contacts: List[Contact] = []
        seen = set()
        for i in range(min(400, anchors.count())):
            a = anchors.nth(i)
            try:
                href = a.get_attribute("href")
                if not href or "/in/" not in href:
                    continue
                full_url = href if href.startswith("http") else "https://www.linkedin.com" + href
                if full_url in seen:
                    continue
                text = (a.inner_text() or "").strip()
                name = text.split("\n")[0].strip()
                if not name:
                    continue
                contacts.append(Contact(name=name, profile_url=full_url))
                seen.add(full_url)
                if len(contacts) >= max_contacts:
                    break
            except Exception:
                continue

        if scrape_emails:
            for idx, c in enumerate(contacts, start=1):
                try:
                    page.goto(c.profile_url, wait_until="domcontentloaded")
                    if screenshots_dir:
                        screenshots_dir.mkdir(parents=True, exist_ok=True)
                        page.screenshot(path=str(screenshots_dir / f"contact_{idx:03d}.png"), full_page=True)

                    mailto = page.locator('a[href^="mailto:"]').first
                    if mailto.count() > 0:
                        href = mailto.get_attribute("href") or ""
                        m = re.match(r"mailto:([^?]+)", href, flags=re.I)
                        if m:
                            c.email = m.group(1).strip()
                except Exception:
                    # Email scraping is best-effort; don't fail the whole run.
                    continue

        return contacts

    @staticmethod
    def write_contacts_csv(contacts: List[Contact], path: Path) -> None:
        path.parent.mkdir(parents=True, exist_ok=True)
        with path.open("w", newline="") as f:
            writer = csv.DictWriter(f, fieldnames=["name", "profile_url", "email"])
            writer.writeheader()
            for c in contacts:
                writer.writerow({"name": c.name, "profile_url": c.profile_url, "email": c.email or ""})

