"""
Base sync service — abstract class all data source sync services extend.

Handles: job creation, completion tracking, error logging, user matching, completion upsert.
Subclasses implement _execute_sync() with source-specific logic.
"""

from abc import ABC, abstractmethod
from dataclasses import dataclass, field
from datetime import datetime, timezone
from uuid import UUID

from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession

from app.config import get_settings
from app.models.sync import DataSourceConfig, SyncJob
from app.models.user import User


@dataclass
class SyncResult:
    records_processed: int = 0
    records_created: int = 0
    records_updated: int = 0
    records_failed: int = 0
    errors: list[str] = field(default_factory=list)


class BaseSyncService(ABC):
    source_name: str  # Must match data_source_configs.name

    async def sync(self, db: AsyncSession, config: DataSourceConfig) -> SyncResult:
        """Run a full sync cycle."""
        job = SyncJob(
            data_source_id=config.id,
            status="running",
            triggered_by="manual",
        )
        db.add(job)
        await db.flush()

        try:
            settings = get_settings()
            if settings.app_env == "development" and not self._has_credentials(config):
                result = await self._generate_demo_data(db, config)
            else:
                result = await self._execute_sync(db, config)

            job.status = "failed" if result.records_failed > 0 and result.records_created == 0 else (
                "partial" if result.records_failed > 0 else "success"
            )
            job.completed_at = datetime.now(timezone.utc)
            job.records_processed = result.records_processed
            job.records_created = result.records_created
            job.records_updated = result.records_updated
            job.records_failed = result.records_failed
            if result.errors:
                job.error_log = "\n".join(result.errors[:50])

            config.last_sync_at = datetime.now(timezone.utc)
            config.last_sync_status = job.status
            config.last_sync_records = result.records_processed

            return result

        except Exception as e:
            job.status = "failed"
            job.completed_at = datetime.now(timezone.utc)
            job.error_log = str(e)
            config.last_sync_status = "failed"
            raise

    @abstractmethod
    async def _execute_sync(self, db: AsyncSession, config: DataSourceConfig) -> SyncResult:
        """Subclasses implement source-specific sync logic."""
        ...

    async def _generate_demo_data(self, db: AsyncSession, config: DataSourceConfig) -> SyncResult:
        """Override in subclass to provide realistic demo data when no API credentials."""
        return SyncResult()

    def _has_credentials(self, config: DataSourceConfig) -> bool:
        """Check if the config has real API credentials set."""
        cfg = config.config or {}
        return any(v for v in cfg.values() if v and str(v).strip())

    async def _match_user_by_email(self, db: AsyncSession, email: str) -> User | None:
        """Match an external user to a DWA employee by email address."""
        result = await db.execute(
            select(User).where(User.email == email.lower().strip())
        )
        return result.scalar_one_or_none()
