"""Salesforce Tooling API client for subscriber org access."""

import requests
from typing import Optional
from datetime import datetime, timedelta

API_VERSION = "59.0"


class SalesforceClient:
    """Wraps the Salesforce REST & Tooling APIs using a raw session ID."""

    def __init__(self, session_id: str, instance_url: Optional[str] = None):
        self.session_id = session_id
        self.org_id = session_id.split("!")[0] if "!" in session_id else None
        self.instance_url = instance_url
        self.headers = {
            "Authorization": f"Bearer {session_id}",
            "Content-Type": "application/json",
        }
        self._user_info: Optional[dict] = None

    # ── connection ───────────────────────────────────────────────────────

    def resolve_instance(self) -> str:
        """Resolve the instance URL using OAuth userinfo or direct identity."""
        import logging
        logger = logging.getLogger("sf_client")

        candidates = []
        if self.instance_url:
            candidates.append(self.instance_url.rstrip("/"))
        candidates.append("https://login.salesforce.com")
        candidates.append("https://test.salesforce.com")

        for base in candidates:
            userinfo_url = f"{base}/services/oauth2/userinfo"
            logger.info(f"Trying userinfo: {userinfo_url}")
            try:
                r = requests.get(userinfo_url, headers=self.headers, timeout=15)
                logger.info(f"  Response: {r.status_code}")
                if r.ok:
                    data = r.json()
                    logger.info(f"  userinfo keys: {list(data.keys())}")
                    profile_url = data.get("profile", "")
                    urls = data.get("urls", {})
                    if profile_url:
                        self.instance_url = "/".join(profile_url.split("/")[:3])
                        self._user_info = {
                            "username": data.get("preferred_username", ""),
                            "display_name": data.get("name", ""),
                            "user_id": data.get("user_id", ""),
                        }
                        logger.info(f"  Resolved instance: {self.instance_url}")
                        return self.instance_url
                    elif urls:
                        partner = urls.get("partner", "")
                        if partner:
                            self.instance_url = partner.split("/services")[0]
                            self._user_info = data
                            logger.info(f"  Resolved instance from urls: {self.instance_url}")
                            return self.instance_url
                else:
                    logger.info(f"  Body: {r.text[:200]}")
            except Exception as e:
                logger.error(f"  Exception: {e}")
                continue

        raise ConnectionError(
            "Could not resolve instance URL. Please provide it explicitly."
        )

    def validate(self) -> dict:
        """Validate the session and return org/user information."""
        if self.instance_url:
            self.instance_url = self.instance_url.rstrip("/")
        else:
            self.resolve_instance()

        url = f"{self.instance_url}/services/data/v{API_VERSION}/limits"
        import logging
        logger = logging.getLogger("sf_client")
        logger.info(f"Validating session against: {url}")
        logger.info(f"Org ID: {self.org_id}")
        logger.info(f"Session prefix: {self.session_id[:30]}...")
        try:
            r = requests.get(url, headers=self.headers, timeout=15)
        except Exception as e:
            logger.error(f"Request exception: {e}")
            raise ConnectionError(f"Network error connecting to {self.instance_url}: {e}")
        logger.info(f"Response status: {r.status_code}")
        if not r.ok:
            body = r.text[:500]
            logger.error(f"Validation failed: HTTP {r.status_code} - {body}")
            raise ConnectionError(
                f"Session validation failed (HTTP {r.status_code}): {body}"
            )
        limits = r.json()

        user_info = self._user_info or {}
        return {
            "org_id": self.org_id,
            "instance_url": self.instance_url,
            "username": user_info.get("username", ""),
            "display_name": user_info.get("display_name", ""),
            "user_id": user_info.get("user_id", ""),
            "api_remaining": limits.get("DailyApiRequests", {}).get("Remaining"),
            "api_max": limits.get("DailyApiRequests", {}).get("Max"),
        }

    # ── tooling helpers ──────────────────────────────────────────────────

    def _tooling_query(self, soql: str) -> list[dict]:
        url = f"{self.instance_url}/services/data/v{API_VERSION}/tooling/query/"
        records: list[dict] = []
        params = {"q": soql}
        while True:
            r = requests.get(url, headers=self.headers, params=params, timeout=30)
            r.raise_for_status()
            data = r.json()
            records.extend(data.get("records", []))
            next_url = data.get("nextRecordsUrl")
            if not next_url:
                break
            url = f"{self.instance_url}{next_url}"
            params = {}
        return records

    def _rest_query(self, soql: str) -> list[dict]:
        url = f"{self.instance_url}/services/data/v{API_VERSION}/query/"
        records: list[dict] = []
        params = {"q": soql}
        while True:
            r = requests.get(url, headers=self.headers, params=params, timeout=30)
            r.raise_for_status()
            data = r.json()
            records.extend(data.get("records", []))
            next_url = data.get("nextRecordsUrl")
            if not next_url:
                break
            url = f"{self.instance_url}{next_url}"
            params = {}
        return records

    # ── logs ─────────────────────────────────────────────────────────────

    def list_logs(
        self,
        operation: Optional[str] = None,
        user_name: Optional[str] = None,
        start_date: Optional[str] = None,
        end_date: Optional[str] = None,
        limit: int = 200,
    ) -> list[dict]:
        clauses = []
        if operation:
            clauses.append(f"Operation LIKE '%{operation}%'")
        if user_name:
            clauses.append(f"LogUser.Name LIKE '%{user_name}%'")
        if start_date:
            clauses.append(f"StartTime >= {start_date}T00:00:00Z")
        if end_date:
            clauses.append(f"StartTime <= {end_date}T23:59:59Z")

        where = f" WHERE {' AND '.join(clauses)}" if clauses else ""
        soql = (
            "SELECT Id, LogUser.Name, LogLength, Operation, Status, "
            "StartTime, DurationMilliseconds, Request "
            f"FROM ApexLog{where} ORDER BY StartTime DESC LIMIT {limit}"
        )
        rows = self._tooling_query(soql)
        return [
            {
                "id": r["Id"],
                "user": (r.get("LogUser") or {}).get("Name", ""),
                "size": r["LogLength"],
                "operation": r["Operation"],
                "status": r["Status"],
                "start_time": r["StartTime"],
                "duration_ms": r["DurationMilliseconds"],
                "request": r.get("Request", ""),
            }
            for r in rows
        ]

    def get_log_body(self, log_id: str) -> str:
        url = (
            f"{self.instance_url}/services/data/v{API_VERSION}"
            f"/tooling/sobjects/ApexLog/{log_id}/Body"
        )
        r = requests.get(url, headers=self.headers, timeout=60)
        r.raise_for_status()
        return r.text

    # ── users ────────────────────────────────────────────────────────────

    def list_users(self) -> list[dict]:
        rows = self._rest_query(
            "SELECT Id, Name, Username, IsActive FROM User "
            "WHERE IsActive = true ORDER BY Name LIMIT 200"
        )
        return [
            {
                "id": r["Id"],
                "name": r["Name"],
                "username": r["Username"],
            }
            for r in rows
        ]

    # ── trace flags ──────────────────────────────────────────────────────

    def list_trace_flags(self) -> list[dict]:
        rows = self._tooling_query(
            "SELECT Id, TracedEntity.Name, LogType, StartDate, ExpirationDate, "
            "DebugLevel.DeveloperName FROM TraceFlag ORDER BY StartDate DESC"
        )
        return [
            {
                "id": r["Id"],
                "traced_entity": (r.get("TracedEntity") or {}).get("Name", ""),
                "log_type": r["LogType"],
                "start_date": r["StartDate"],
                "expiration_date": r["ExpirationDate"],
                "debug_level": (r.get("DebugLevel") or {}).get("DeveloperName", ""),
            }
            for r in rows
        ]

    def list_debug_levels(self) -> list[dict]:
        rows = self._tooling_query(
            "SELECT Id, DeveloperName, MasterLabel FROM DebugLevel ORDER BY DeveloperName"
        )
        return [{"id": r["Id"], "name": r["DeveloperName"], "label": r["MasterLabel"]} for r in rows]

    def create_trace_flag(
        self,
        traced_entity_id: str,
        debug_level_id: str,
        duration_minutes: int = 60,
    ) -> dict:
        now = datetime.utcnow()
        payload = {
            "TracedEntityId": traced_entity_id,
            "DebugLevelId": debug_level_id,
            "LogType": "USER_DEBUG",
            "StartDate": now.isoformat() + "+0000",
            "ExpirationDate": (now + timedelta(minutes=duration_minutes)).isoformat() + "+0000",
        }
        url = f"{self.instance_url}/services/data/v{API_VERSION}/tooling/sobjects/TraceFlag"
        r = requests.post(url, headers=self.headers, json=payload, timeout=15)
        r.raise_for_status()
        return r.json()

    def delete_trace_flag(self, flag_id: str) -> bool:
        url = f"{self.instance_url}/services/data/v{API_VERSION}/tooling/sobjects/TraceFlag/{flag_id}"
        r = requests.delete(url, headers=self.headers, timeout=15)
        return r.status_code == 204

    def create_debug_level(
        self,
        developer_name: str,
        label: str,
        apex_code: str = "FINE",
        apex_profiling: str = "NONE",
        callout: str = "INFO",
        database: str = "INFO",
        system: str = "INFO",
        validation: str = "INFO",
        visualforce: str = "INFO",
        workflow: str = "INFO",
        nba: str = "NONE",
        wave: str = "NONE",
    ) -> dict:
        payload = {
            "DeveloperName": developer_name,
            "MasterLabel": label,
            "ApexCode": apex_code,
            "ApexProfiling": apex_profiling,
            "Callout": callout,
            "Database": database,
            "System": system,
            "Validation": validation,
            "Visualforce": visualforce,
            "Workflow": workflow,
            "Nba": nba,
            "Wave": wave,
        }
        url = f"{self.instance_url}/services/data/v{API_VERSION}/tooling/sobjects/DebugLevel"
        r = requests.post(url, headers=self.headers, json=payload, timeout=15)
        r.raise_for_status()
        return r.json()
