from __future__ import annotations

import json
from datetime import datetime
from typing import Any

from app.db import execute, fetch_all, fetch_one

PLATFORMS = ("twitter", "linkedin", "instagram", "facebook", "tiktok", "pinterest")

_REQUIRED_FIELDS: dict[str, tuple[str, ...]] = {
    "twitter": ("api_key", "api_secret", "access_token", "access_secret"),
    "linkedin": ("access_token", "person_urn"),
    "instagram": ("access_token", "page_id"),
    "facebook": ("access_token", "page_id"),
    "tiktok": ("access_token", "open_id"),
    "pinterest": ("access_token", "board_id"),
}


def _normalize_platform(platform: str) -> str:
    value = (platform or "").strip().lower()
    if value not in PLATFORMS:
        raise ValueError(f"Unsupported platform: {platform}")
    return value


def _serialize(value: Any) -> Any:
    if hasattr(value, "isoformat"):
        return value.isoformat()
    return value


def _normalize_config(row: dict[str, Any] | None) -> dict[str, Any]:
    if not row:
        return {}
    config = row.get("config") or {}
    if isinstance(config, str):
        try:
            config = json.loads(config)
        except Exception:
            config = {}
    if not isinstance(config, dict):
        config = {}
    # Keep compatibility with schemas that store fields as columns.
    for key in ("api_key", "api_secret", "access_token", "access_secret", "person_urn", "page_id", "open_id", "board_id"):
        if row.get(key) and not config.get(key):
            config[key] = row.get(key)
    return config


def _has_credentials(platform: str, config: dict[str, Any]) -> bool:
    required = _REQUIRED_FIELDS.get(platform, ())
    if not required:
        return False
    return all(bool(config.get(name)) for name in required)


def _log_event(title: str, body: str, status: str, metadata: dict[str, Any]) -> None:
    try:
        execute(
            """
            INSERT INTO agent_events (agent_name, agent_type, event_type, title, body, status, channel, metadata)
            VALUES (%s, %s, %s, %s, %s, %s, %s, %s::jsonb)
            """,
            (
                "marketing_automation",
                "social_publish",
                "social_publish",
                title,
                body[:2000],
                status,
                "marketing",
                json.dumps(metadata),
            ),
        )
    except Exception:
        pass


def get_integration(platform: str) -> dict[str, Any] | None:
    platform = _normalize_platform(platform)
    row = fetch_one(
        "SELECT * FROM social_integrations WHERE platform = %s AND COALESCE(is_active, TRUE) = TRUE",
        (platform,),
    )
    if not row:
        return None
    out = {k: _serialize(v) for k, v in row.items()}
    out["platform"] = platform
    out["config"] = _normalize_config(row)
    return out


def get_configured_channels() -> list[dict[str, Any]]:
    rows = fetch_all(
        "SELECT * FROM social_integrations WHERE platform = ANY(%s) ORDER BY platform",
        (list(PLATFORMS),),
    )
    by_platform = {(row.get("platform") or "").lower(): row for row in rows}
    items: list[dict[str, Any]] = []
    for platform in PLATFORMS:
        row = by_platform.get(platform)
        config = _normalize_config(row)
        items.append(
            {
                "platform": platform,
                "configured": _has_credentials(platform, config),
                "is_active": bool(row.get("is_active")) if row else False,
                "updated_at": _serialize(row.get("updated_at")) if row else None,
            }
        )
    return items


def publish_to_channel(platform: str, text: str, image_path: str | None = None, image_url: str | None = None) -> dict[str, Any]:
    try:
        platform = _normalize_platform(platform)
    except ValueError as exc:
        return {"status": "failed", "error": str(exc), "platform": platform}
    integration = get_integration(platform)
    if not integration:
        return {
            "status": "skipped_not_configured",
            "error": f"{platform} integration is not configured",
            "platform": platform,
        }
    config = integration.get("config") or {}
    if not _has_credentials(platform, config):
        return {
            "status": "skipped_not_configured",
            "error": f"Missing credentials for {platform}",
            "platform": platform,
        }

    try:
        if platform == "twitter":
            try:
                import tweepy  # type: ignore
            except Exception as exc:
                return {"status": "failed_dependency", "platform": platform, "error": f"tweepy unavailable: {exc}"}
            client = tweepy.Client(
                consumer_key=config["api_key"],
                consumer_secret=config["api_secret"],
                access_token=config["access_token"],
                access_token_secret=config["access_secret"],
            )
            resp = client.create_tweet(text=text[:280])
            return {"status": "published", "platform": platform, "external_id": str(getattr(resp, "data", {}) or {})}

        if platform == "linkedin":
            import requests

            payload = {
                "author": config.get("person_urn"),
                "lifecycleState": "PUBLISHED",
                "specificContent": {
                    "com.linkedin.ugc.ShareContent": {
                        "shareCommentary": {"text": text},
                        "shareMediaCategory": "IMAGE" if image_url else "NONE",
                    }
                },
                "visibility": {"com.linkedin.ugc.MemberNetworkVisibility": "PUBLIC"},
            }
            if image_url:
                payload["specificContent"]["com.linkedin.ugc.ShareContent"]["media"] = [{"status": "READY", "originalUrl": image_url}]
            r = requests.post(
                "https://api.linkedin.com/v2/ugcPosts",
                headers={"Authorization": f"Bearer {config['access_token']}", "X-Restli-Protocol-Version": "2.0.0"},
                json=payload,
                timeout=20,
            )
            return {"status": "published" if r.ok else "failed", "platform": platform, "response_code": r.status_code, "error": None if r.ok else r.text[:300]}

        if platform in ("instagram", "facebook"):
            import requests

            endpoint = f"https://graph.facebook.com/v20.0/{config['page_id']}/feed"
            payload = {"message": text, "access_token": config["access_token"]}
            if image_url:
                endpoint = f"https://graph.facebook.com/v20.0/{config['page_id']}/photos"
                payload = {"url": image_url, "caption": text, "access_token": config["access_token"]}
            r = requests.post(endpoint, data=payload, timeout=20)
            data = {}
            try:
                data = r.json()
            except Exception:
                data = {}
            return {
                "status": "published" if r.ok else "failed",
                "platform": platform,
                "external_id": data.get("id"),
                "response_code": r.status_code,
                "error": None if r.ok else (data.get("error", {}).get("message") or r.text[:300]),
            }

        if platform == "pinterest":
            import requests

            payload = {"board_id": config.get("board_id"), "title": text[:100], "description": text, "media_source": {"source_type": "image_url", "url": image_url}}
            r = requests.post(
                "https://api.pinterest.com/v5/pins",
                headers={"Authorization": f"Bearer {config['access_token']}", "Content-Type": "application/json"},
                json=payload,
                timeout=20,
            )
            return {"status": "published" if r.ok else "failed", "platform": platform, "response_code": r.status_code, "error": None if r.ok else r.text[:300]}

        if platform == "tiktok":
            return {
                "status": "failed",
                "platform": platform,
                "error": "TikTok publish placeholder not implemented yet (requires creator upload flow)",
            }
    except Exception as exc:
        return {"status": "failed", "platform": platform, "error": str(exc)}

    return {"status": "failed", "platform": platform, "error": "Unsupported platform"}


def test_connection(platform: str, integration: dict[str, Any] | None = None) -> dict[str, Any]:
    platform = _normalize_platform(platform)
    integration = integration or get_integration(platform)
    if not integration:
        return {"ok": False, "status": "skipped_not_configured", "error": f"{platform} integration is not configured"}
    config = integration.get("config") or {}
    if not _has_credentials(platform, config):
        return {"ok": False, "status": "skipped_not_configured", "error": f"Missing credentials for {platform}"}
    # Keep tests lightweight: perform a dry publish without side effects where possible.
    if platform == "twitter":
        try:
            import tweepy  # type: ignore

            client = tweepy.Client(
                consumer_key=config["api_key"],
                consumer_secret=config["api_secret"],
                access_token=config["access_token"],
                access_token_secret=config["access_secret"],
            )
            _ = client.get_me()
            return {"ok": True, "status": "ok", "message": "Twitter credentials look valid"}
        except Exception as exc:
            return {"ok": False, "status": "failed", "error": str(exc)}
    return {"ok": True, "status": "ok", "message": f"{platform} configuration is present"}


def run_publish_job(
    job_id: int,
    text: str,
    channels: list[str],
    image_url: str | None,
    media_ids: list[int],
    project_id: int | None = None,
) -> None:
    started_at = datetime.utcnow()
    execute(
        "UPDATE social_publish_jobs SET status=%s, started_at=NOW(), updated_at=NOW() WHERE id=%s",
        ("running", job_id),
    )
    _log_event(
        title=f"Social publish job #{job_id} gestart",
        body=f"Kanalen: {', '.join(channels) if channels else '-'}",
        status="running",
        metadata={"job_id": job_id, "channels": channels},
    )

    chosen_image_url = image_url
    if not chosen_image_url and media_ids:
        media_rows = fetch_all(
            "SELECT id, media_url, url, file_path FROM marketing_media WHERE id = ANY(%s) ORDER BY id",
            (media_ids,),
        )
        if media_rows:
            first = media_rows[0]
            chosen_image_url = first.get("media_url") or first.get("url")

    results: list[dict[str, Any]] = []
    for channel in channels:
        result = publish_to_channel(channel, text=text, image_url=chosen_image_url)
        results.append(result)

    published = sum(1 for item in results if item.get("status") == "published")
    skipped = sum(1 for item in results if item.get("status") == "skipped_not_configured")
    failed = len(results) - published - skipped

    final_status = "completed"
    if published == 0 and failed > 0:
        final_status = "failed"
    elif failed > 0:
        final_status = "completed_with_errors"

    execute(
        """
        UPDATE social_publish_jobs
        SET status=%s,
            finished_at=NOW(),
            updated_at=NOW(),
            result=%s::jsonb
        WHERE id=%s
        """,
        (
            final_status,
            json.dumps(
                {
                    "published": published,
                    "skipped": skipped,
                    "failed": failed,
                    "channels": channels,
                    "results": results,
                    "started_at": started_at.isoformat(),
                }
            ),
            job_id,
        ),
    )
    _log_event(
        title=f"Social publish job #{job_id} afgerond",
        body=f"Published={published}, skipped={skipped}, failed={failed}",
        status=final_status,
        metadata={"job_id": job_id, "results": results, "project_id": project_id},
    )

    try:
        from app.services import projects as project_svc

        project_svc.register_agent_output(
            asset_type="social_publish",
            title=f"Social publish #{job_id} · {final_status}",
            ref_id=str(job_id),
            payload={
                "job_id": job_id,
                "status": final_status,
                "channels": channels,
                "published": published,
                "text_preview": (text or "")[:200],
            },
            project_id=project_id,
            source_agent="marketing",
            created_by="marketing_automation",
        )
    except Exception:
        pass
