"""Cloud Dynamic Offsets Auto-Sync for Poe2VisualTool.

Allows seamless offset updates when Path of Exile 2 receives minor patches
without requiring users to re-download the entire application.
"""

from __future__ import annotations

import json
import logging
import os
import shutil
import urllib.error
import urllib.request
from dataclasses import dataclass
from pathlib import Path
from typing import Any, Dict, Optional

logger = logging.getLogger("Poe2VisualTool.CloudSync")

DEFAULT_OFFSETS_URL = (
    "https://raw.githubusercontent.com/mirsixabcf2-commits/Poe2VisualTool/main/offsets.toml"
)


@dataclass
class SyncResult:
    success: bool
    message: str
    updated: bool = False
    remote_version: str = ""
    local_version: str = ""


class CloudOffsetsSync:
    """Manages downloading, checking, and updating offsets.toml from a remote source."""

    def __init__(
        self,
        local_config_path: Optional[Path] = None,
        remote_url: str = DEFAULT_OFFSETS_URL,
        timeout_seconds: float = 3.5,
    ) -> None:
        self.local_path = local_config_path or Path("offsets.toml")
        self.remote_url = remote_url
        self.timeout = timeout_seconds

    def read_local_fingerprint(self) -> str:
        """Reads local version/fingerprint from offsets.toml or config.toml."""
        if not self.local_path.exists():
            return ""
        try:
            with open(self.local_path, "r", encoding="utf-8") as f:
                content = f.read()
            for line in content.splitlines():
                stripped = line.strip()
                if stripped.startswith("version") or stripped.startswith("timestamp"):
                    parts = stripped.split("=", 1)
                    if len(parts) == 2:
                        return parts[1].strip().strip('"').strip("'")
            # If no version tag, return file length + mtime hash
            return f"len_{len(content)}"
        except Exception as err:
            logger.warning("Failed to read local offsets fingerprint: %s", err)
            return ""

    def fetch_remote_offsets(self, custom_url: Optional[str] = None) -> tuple[bool, str, str]:
        """Fetches remote offsets content.

        Returns (success, content, error_msg).
        """
        url = custom_url or self.remote_url
        req = urllib.request.Request(
            url,
            headers={
                "User-Agent": "Poe2VisualTool-Sync/1.2.0 (Windows NT 10.0; Win64; x64)",
                "Accept": "text/plain, application/toml, text/toml",
            },
        )
        try:
            with urllib.request.urlopen(req, timeout=self.timeout) as response:
                if response.status == 200:
                    raw_data = response.read().decode("utf-8", errors="replace")
                    return True, raw_data, ""
                return False, "", f"HTTP {response.status}"
        except urllib.error.URLError as err:
            return False, "", f"Network error: {err}"
        except Exception as err:
            return False, "", f"Unexpected error: {err}"

    def extract_remote_fingerprint(self, content: str) -> str:
        """Parses remote version/timestamp from toml content."""
        for line in content.splitlines():
            stripped = line.strip()
            if stripped.startswith("version") or stripped.startswith("timestamp"):
                parts = stripped.split("=", 1)
                if len(parts) == 2:
                    return parts[1].strip().strip('"').strip("'")
        return f"len_{len(content)}"

    def sync(self, custom_url: Optional[str] = None) -> SyncResult:
        """Compares local vs remote offsets and safely updates if remote is newer."""
        local_fp = self.read_local_fingerprint()
        success, content, err = self.fetch_remote_offsets(custom_url)
        if not success:
            return SyncResult(
                success=False,
                message=f"Cloud sync unreachable: {err}",
                updated=False,
                local_version=local_fp,
            )

        remote_fp = self.extract_remote_fingerprint(content)
        if local_fp and local_fp == remote_fp:
            return SyncResult(
                success=True,
                message="Offsets are already up-to-date",
                updated=False,
                local_version=local_fp,
                remote_version=remote_fp,
            )

        # Validate minimum content sanity before writing
        if len(content) < 30 or ("[" not in content and "=" not in content):
            return SyncResult(
                success=False,
                message="Invalid offsets content received from cloud",
                updated=False,
                local_version=local_fp,
            )

        # Backup existing file if present
        if self.local_path.exists():
            backup_path = self.local_path.with_suffix(".toml.bak")
            try:
                shutil.copy2(self.local_path, backup_path)
            except Exception as backup_err:
                logger.warning("Could not create backup of offsets.toml: %s", backup_err)

        # Write new content atomically
        temp_path = self.local_path.with_suffix(".tmp")
        try:
            with open(temp_path, "w", encoding="utf-8") as f:
                f.write(content)
            temp_path.replace(self.local_path)
            return SyncResult(
                success=True,
                message="Offsets successfully updated from cloud",
                updated=True,
                local_version=local_fp,
                remote_version=remote_fp,
            )
        except Exception as write_err:
            if temp_path.exists():
                temp_path.unlink()
            return SyncResult(
                success=False,
                message=f"Failed to write updated offsets: {write_err}",
                updated=False,
                local_version=local_fp,
            )
