"""
Google Sheets Writer Service for Scheduled Jobs
Writes Salesforce data back to Google Sheets via Apps Script Web App endpoint
"""

import logging
import httpx
from typing import List, Dict, Any, Optional
from datetime import datetime
from uuid import uuid4

from .. import settings

log = logging.getLogger(__name__)


class SheetsWriteError(Exception):
    """Raised when writing to Sheets fails"""

    pass


class SheetsWriter:
    """
    Writes data to Google Sheets by calling Apps Script endpoints.

    The Google Sheets Add-on must be installed and configured with an API key
    for the account owning the scheduled job.
    """

    def __init__(
        self,
        sheet_id: str,
        account_id: str,
        api_key: str,
        api_base_url: Optional[str] = None,
    ):
        """
        Initialize Sheets writer.

        Args:
            sheet_id: Google Sheets spreadsheet ID
            account_id: Owning workspace/account ID (used for multi-tenant scoping)
            api_key: User's API key for authentication
            api_base_url: Optional API base URL (defaults to FM_DEFAULT_APP_ORIGIN from settings)
        """
        # IMPORTANT: scheduled-results is a shared polling channel. Redis keys must be scoped
        # by account_id to prevent cross-tenant reads/acks by guessing a sheet_id.
        self.sheet_id = str(sheet_id or "").strip()
        self.account_id = str(account_id or "").strip()
        if not self.sheet_id:
            raise ValueError("sheet_id is required")
        if not self.account_id:
            raise ValueError("account_id is required")
        if ":" in self.sheet_id:
            raise ValueError("sheet_id must not contain ':'")
        if ":" in self.account_id:
            raise ValueError("account_id must not contain ':'")
        self.api_key = api_key
        self.api_base_url = api_base_url

    async def write_to_sheet(
        self,
        sheet_name: str,
        headers: List[str],
        rows: List[List[Any]],
        metadata: Optional[Dict[str, Any]] = None,
    ) -> Dict[str, Any]:
        """
        Write data to a Google Sheet using a callback endpoint.

        The Add-on must expose a doPost endpoint that accepts:
        {
            "action": "write_scheduled_data",
            "sheet_name": "...",
            "headers": [...],
            "rows": [[...], ...],
            "metadata": {...}
        }

        Args:
            sheet_name: Name of the sheet tab to write to
            headers: Column headers
            rows: Data rows (list of lists)
            metadata: Optional metadata (job_id, query, etc.)

        Returns:
            Response from Apps Script endpoint

        Raises:
            SheetsWriteError: If write fails
        """
        if not headers or not rows:
            log.warning(f"Empty data for sheet {sheet_name}, skipping write")
            return {"ok": True, "rows": 0, "message": "No data to write"}

        try:
            # Build payload for Apps Script endpoint
            payload = {
                "action": "write_scheduled_data",
                "sheet_id": self.sheet_id,
                "sheet_name": sheet_name,
                "headers": headers,
                "rows": rows[:10000],  # Limit to 10k rows for safety
                "metadata": metadata or {},
                "timestamp": datetime.utcnow().isoformat(),
            }

            # Call the Apps Script Web App endpoint
            # The Add-on should have a doPost function that handles this
            async with httpx.AsyncClient(timeout=60.0) as client:
                # Try direct callback first (if Add-on exposes a web app URL)
                # For now, we'll use a webhook pattern where the user configures a callback URL

                # FALLBACK: Use backend endpoint that notifies user to refresh
                # This is a temporary solution until direct Apps Script callbacks are set up

                log.info(
                    f"Writing {len(rows)} rows to sheet {sheet_name} (sheet_id: {self.sheet_id})"
                )

                # Store the result in a staging area that the Add-on can poll
                # This avoids needing to expose Apps Script as a web service
                return await self._store_for_polling(payload)

        except Exception as e:
            log.error(f"Failed to write to sheet {sheet_name}: {e}")
            raise SheetsWriteError(f"Sheet write failed: {str(e)}") from e

    async def _store_for_polling(
        self,
        payload: Dict[str, Any],
        ttl_seconds: int = 3600,
        key_suffix: Optional[str] = None,
    ) -> Dict[str, Any]:
        """
        Store scheduled job results in Redis/DB for the Add-on to poll.

        This is a pull-based approach where the Add-on periodically checks
        for new scheduled results and writes them to Sheets.

        This avoids the complexity of exposing Apps Script as a web service.
        """
        # Import here to avoid circular dependencies
        from ..api.progress import get_redis

        try:
            redis_conn = await get_redis()
            if not redis_conn:
                log.warning("Redis not available for job result storage")
                return {"ok": False, "error": "Redis not configured"}

            # Store under a key the Add-on can poll
            key = f"scheduled_result:{self.account_id}:{self.sheet_id}:{payload['sheet_name']}"
            if key_suffix:
                key = f"{key}:{key_suffix}"
            value = {
                "sheet_name": payload["sheet_name"],
                "headers": payload["headers"],
                "rows": payload["rows"],
                "metadata": payload["metadata"],
                "created_at": payload["timestamp"],
                "status": "pending_write",
            }

            import json

            await redis_conn.setex(key, max(60, int(ttl_seconds)), json.dumps(value))

            # Also add to a list of pending results for this sheet
            list_key = f"scheduled_results:{self.account_id}:{self.sheet_id}"
            await redis_conn.lpush(list_key, key)
            await redis_conn.expire(list_key, 86400)  # 24 hour TTL

            log.info(f"Stored scheduled result for polling: {key}")

            return {
                "ok": True,
                "rows": len(payload["rows"]),
                "sheet_name": payload["sheet_name"],
                "storage_key": key,
                "message": "Result stored for Add-on to retrieve",
            }

        except Exception as e:
            log.error(f"Failed to store result for polling: {e}")
            return {"ok": False, "error": str(e)}

    async def store_enrichment_results(
        self,
        sheet_name: str,
        results: List[Dict[str, Any]],
        config: Dict[str, Any],
        metadata: Optional[Dict[str, Any]] = None,
    ) -> Dict[str, Any]:
        """
        Store enrichment results for the Sheets add-on poller.

        Uses the scheduled-results polling channel with a custom payload.
        """
        payload = {
            "action": "write_scheduled_data",
            "sheet_id": self.sheet_id,
            "sheet_name": sheet_name,
            "headers": [],
            "rows": [],
            "metadata": {
                "mode": "byo_enrichment",
                "results": results,
                "config": config,
                **(metadata or {}),
            },
            "timestamp": datetime.utcnow().isoformat(),
        }

        return await self._store_for_polling(
            payload,
            ttl_seconds=settings.BYO_ENRICHMENT_ROW_TTL_SECONDS,
            key_suffix=f"enrichment:{uuid4().hex}",
        )

    async def store_transform_results(
        self,
        sheet_name: str,
        results: List[Dict[str, Any]],
        config: Dict[str, Any],
        metadata: Optional[Dict[str, Any]] = None,
    ) -> Dict[str, Any]:
        """
        Store Gremlin transform results for the Sheets add-on poller.

        Uses the scheduled-results polling channel with a custom payload.
        """
        payload = {
            "action": "write_scheduled_data",
            "sheet_id": self.sheet_id,
            "sheet_name": sheet_name,
            "headers": [],
            "rows": [],
            "metadata": {
                "mode": "gremlin_transform",
                "results": results,
                "config": config,
                **(metadata or {}),
            },
            "timestamp": datetime.utcnow().isoformat(),
        }

        return await self._store_for_polling(
            payload,
            ttl_seconds=settings.BYO_ENRICHMENT_ROW_TTL_SECONDS,
            key_suffix=f"transform:{uuid4().hex}",
        )

    async def store_scraper_results(
        self,
        sheet_name: str,
        results: List[Dict[str, Any]],
        config: Dict[str, Any],
        metadata: Optional[Dict[str, Any]] = None,
    ) -> Dict[str, Any]:
        """
        Store scraper results for the Sheets add-on poller.
        """
        payload = {
            "action": "write_scheduled_data",
            "sheet_id": self.sheet_id,
            "sheet_name": sheet_name,
            "headers": [],
            "rows": [],
            "metadata": {
                "mode": "byo_scraper",
                "results": results,
                "config": config,
                **(metadata or {}),
            },
            "timestamp": datetime.utcnow().isoformat(),
        }

        return await self._store_for_polling(
            payload,
            ttl_seconds=settings.SCRAPER_DATA_TTL_SECONDS,
            key_suffix=f"scraper:{uuid4().hex}",
        )


async def write_salesforce_to_sheets(
    sheet_id: str,
    sheet_name: str,
    headers: List[str],
    rows: List[List[Any]],
    account_id: str,
    api_key: str,
    metadata: Optional[Dict[str, Any]] = None,
) -> Dict[str, Any]:
    """
    Convenience function to write Salesforce data to Sheets.

    Args:
        sheet_id: Google Sheets spreadsheet ID
        sheet_name: Sheet tab name
        headers: Column headers
        rows: Data rows
        api_key: User's API key
        metadata: Optional job metadata

    Returns:
        Write result
    """
    writer = SheetsWriter(sheet_id=sheet_id, account_id=account_id, api_key=api_key)
    return await writer.write_to_sheet(sheet_name, headers, rows, metadata)
