from __future__ import annotations

from pathlib import Path
from typing import Any
import sys
import threading

ST_ROOT = Path(__file__).resolve().parents[1]
if str(ST_ROOT) not in sys.path:
    sys.path.insert(0, str(ST_ROOT))

from Shared.storage.sort_storage import (
    default_database_path,
    list_database_files,
    open_database_file,
    create_database_file,
    save_database_as,
    get_post_detail,
    get_post_stats,
    initialize_database,
    list_account_posts,
    list_accounts,
    list_deleted_accounts,
    list_collection_accounts,
    replace_collection_accounts,
    soft_delete_account,
    soft_delete_post,
    upsert_observation,
)


class AnalyzeRepository:
    def __init__(self, st_root: str | Path, db_path: str | Path | None = None) -> None:
        self.st_root = Path(st_root).expanduser().resolve()
        self._explicit_db_path = Path(db_path).expanduser().resolve() if db_path else None
        self._db_lock = threading.RLock()
        self.db_path = self._explicit_db_path or default_database_path(self.st_root)
        initialize_database(self.db_path, self.st_root)

    def health(self) -> dict[str, Any]:
        with self._db_lock:
            return {
                "ok": True,
                "service": "Analyze8780",
                "version": "2.3.8.273",
                "db_path": str(self.db_path),
                "db_filename": self.db_path.name,
            }

    def accounts(self, *, page: int = 1, page_size: int = 50, include_deleted: bool = False) -> dict[str, Any]:
        with self._db_lock:
            return list_accounts(self.db_path, page=page, page_size=page_size, include_deleted=include_deleted)

    def account_posts(self, account_id: int, *, page: int = 1, page_size: int = 50, sort_by: str = "views") -> dict[str, Any]:
        with self._db_lock:
            return list_account_posts(
                self.db_path,
                account_id=account_id,
                page=page,
                page_size=page_size,
                sort_by=sort_by,
            )

    def post(self, post_id: int) -> dict[str, Any] | None:
        with self._db_lock:
            return get_post_detail(self.db_path, post_id)

    def post_stats(self, post_id: int, *, limit: int = 144) -> list[dict[str, Any]]:
        with self._db_lock:
            return get_post_stats(self.db_path, post_id, limit=limit)

    def save_observation(self, payload: dict[str, Any]) -> dict[str, Any]:
        with self._db_lock:
            return upsert_observation(
                self.db_path,
                username=str(payload.get("username") or payload.get("account") or ""),
                platform_account_id=str(payload.get("platform_account_id") or ""),
                display_name=str(payload.get("display_name") or ""),
                profile_url=str(payload.get("profile_url") or ""),
                shortcode=str(payload.get("shortcode") or payload.get("code") or ""),
                post_url=str(payload.get("post_url") or ""),
                caption=str(payload.get("caption") or ""),
                published_at=payload.get("published_at"),
                thumbnail_url=str(payload.get("thumbnail_url") or ""),
                video_url=str(payload.get("video_url") or ""),
                sort_by=str(payload.get("sort_by") or payload.get("sort_field") or ""),
                sort_value=int(payload.get("sort_value") or 0),
                source_kind=str(payload.get("source_kind") or "LOCAL"),
                views=int(payload.get("views") or payload.get("play_count") or 0),
                views_available=int(payload.get("views_available") or 0),
                likes=int(payload.get("likes") or payload.get("like_count") or 0),
                comments=int(payload.get("comments") or payload.get("comment_count") or 0),
                reposts=int(payload.get("reposts") or payload.get("media_repost_count") or 0),
                observed_at=payload.get("observed_at") or payload.get("collected_at") or payload.get("captured_at"),
            )

    def save_observations(self, payload: dict[str, Any]) -> dict[str, Any]:
        items = payload.get("items") if isinstance(payload, dict) else []
        if not isinstance(items, list):
            raise ValueError("items must be an array")
        if len(items) > 500:
            raise ValueError("items limit is 500")
        with self._db_lock:
            saved = []
            skipped = []
            failed = []
            for index, item in enumerate(items):
                if not isinstance(item, dict):
                    failed.append({"index": index, "error": "item must be an object"})
                    continue
                try:
                    result = self.save_observation(item)
                    if result.get("skipped_unchanged"):
                        skipped.append(result)
                    else:
                        saved.append(result)
                except Exception as exc:
                    failed.append({"index": index, "shortcode": str(item.get("shortcode") or item.get("code") or ""), "error": str(exc)})
            return {
                "ok": not failed,
                "saved_count": len(saved),
                "skipped_count": len(skipped),
                "failed_count": len(failed),
                "items": saved,
                "skipped_items": skipped,
                "failures": failed,
            }

    def database_files(self) -> dict[str, Any]:
        with self._db_lock:
            info = list_database_files(self.st_root)
            return {"ok": True, **info, "active_filename": self.db_path.name, "active_path": str(self.db_path)}

    def open_database(self, payload: dict[str, Any]) -> dict[str, Any]:
        if self._explicit_db_path is not None:
            raise ValueError("runtime database switching is disabled when --db is used")
        filename = str(payload.get("filename") or "") if isinstance(payload, dict) else ""
        with self._db_lock:
            result = open_database_file(self.st_root, filename)
            self.db_path = Path(result["db_path"]).resolve()
            return {"ok": True, "action": "open", **result, **self.collection_accounts()}

    def new_database(self, payload: dict[str, Any]) -> dict[str, Any]:
        if self._explicit_db_path is not None:
            raise ValueError("runtime database switching is disabled when --db is used")
        filename = str(payload.get("filename") or "") if isinstance(payload, dict) else ""
        with self._db_lock:
            result = create_database_file(self.st_root, filename)
            self.db_path = Path(result["db_path"]).resolve()
            return {"ok": True, "action": "new", **result, **self.collection_accounts()}

    def save_database_as(self, payload: dict[str, Any]) -> dict[str, Any]:
        if self._explicit_db_path is not None:
            raise ValueError("runtime database switching is disabled when --db is used")
        filename = str(payload.get("filename") or "") if isinstance(payload, dict) else ""
        with self._db_lock:
            result = save_database_as(self.st_root, self.db_path, filename)
            self.db_path = Path(result["db_path"]).resolve()
            return {"ok": True, "action": "save_as", **result, **self.collection_accounts()}

    def collection_accounts(self) -> dict[str, Any]:
        with self._db_lock:
            items = list_collection_accounts(self.db_path, include_deleted=True)
            return {"ok": True, "items": items, "total": len(items), "db_filename": self.db_path.name, "db_path": str(self.db_path)}

    def replace_collection_accounts(self, payload: dict[str, Any]) -> dict[str, Any]:
        items = payload.get("items") if isinstance(payload, dict) else []
        if not isinstance(items, list):
            raise ValueError("items must be an array")
        with self._db_lock:
            result = replace_collection_accounts(self.db_path, items)
            return {"ok": True, **result, "db_filename": self.db_path.name, "db_path": str(self.db_path)}

    def delete_account(self, account_id: int) -> dict[str, Any]:
        with self._db_lock:
            return soft_delete_account(self.db_path, account_id)

    def delete_post(self, post_id: int) -> dict[str, Any]:
        with self._db_lock:
            return soft_delete_post(self.db_path, post_id)

    def deleted_accounts(self) -> list[dict[str, Any]]:
        with self._db_lock:
            return list_deleted_accounts(self.db_path)
