import json
import logging
import mimetypes
import os
import re
import threading
import time
from pathlib import Path
from typing import Any, Callable, Dict, Optional
import requests
from config import DEFAULT_TIMEOUT, DEFAULT_VIDEA_CHUNK_SIZE_MB, USER_AGENT
from converter import EmbedConverter
from database import get_setting, update_account_cookies
from providers.base import BaseVideoProvider, UploadResult
from stream_uploader import (
    DiskStreamingMultipart,
    UploadCancelledException,
    create_fast_session,
)

logger = logging.getLogger("VideaProvider")


class VideaProvider(BaseVideoProvider):
    name = "videa"

    def __init__(self):
        self.converter = EmbedConverter()

    def _create_session(self, account: Optional[Dict[str, Any]] = None) -> requests.Session:
        # High-throughput session with 4MB TCP socket send buffer and 1MB blocksize
        session = create_fast_session(blocksize_mb=1, sndbuf_mb=4)
        session.headers.update({
            "User-Agent": USER_AGENT,
            "Referer": "https://videa.hu/",
        })
        if account and account.get("cookies"):
            try:
                cookies_dict = json.loads(account["cookies"])
                session.cookies.update(cookies_dict)
            except Exception as e:
                logger.warning(f"Could not load cookies for account {account.get('name')}: {e}")
        return session

    def validate_credentials(self, account: Optional[Dict[str, Any]], session: Optional[requests.Session] = None) -> bool:
        """
        Validates whether the Videa session is active.
        If inactive and username/password are provided, attempts auto-login.
        """
        if not account:
            return False
        if not session:
            session = self._create_session(account)
        try:
            resp = session.get("https://videa.hu/feltoltes", timeout=DEFAULT_TIMEOUT, allow_redirects=True)
            if "/belepes" not in resp.url and "form-login" not in resp.text:
                logger.info(f"Videa session valid for account: {account.get('name')}")
                return True
            else:
                logger.warning(f"Videa session invalid/expired for account {account.get('name')} (redirected to: {resp.url})")
        except Exception as e:
            logger.error(f"Error checking Videa session: {e}")

        # Session expired or not logged in: try auto-login
        if account.get("username") and account.get("password"):
            logger.info(f"Attempting auto-login for Videa account: {account.get('name')}")
            return self.login(account, session=session)

        return False

    def login(self, account: Optional[Dict[str, Any]], session: Optional[requests.Session] = None) -> bool:
        """Logs into Videa using username (email) and password."""
        if not account:
            return False
        if not session:
            session = self._create_session(account)

        username = account.get("username")
        password = account.get("password")
        if not username or not password:
            logger.error(f"Missing username/password for account {account.get('name')}")
            return False

        login_url = "https://videa.hu/interface?logcmd=tryLoginByRequest"
        post_json = {
            "cmd": "tryLoginByRequest",
            "userid": username,
            "password": password,
            "connectToken": "",
        }

        try:
            resp = session.post(
                login_url,
                data={"json": json.dumps(post_json)},
                headers={"Referer": "https://videa.hu/belepes/"},
                timeout=DEFAULT_TIMEOUT,
            )
            data = resp.json()
            if data.get("response", {}).get("code") == 0:
                logger.info(f"Successfully logged into Videa account: {account.get('name')}")
                # Save fresh cookies to database
                if account.get("id"):
                    cookies_str = json.dumps(session.cookies.get_dict())
                    update_account_cookies(account["id"], cookies_str)
                    account["cookies"] = cookies_str
                return True
            else:
                msg = data.get("response", {}).get("message", "Ismeretlen hiba")
                logger.warning(f"Videa login failed: {msg} (code: {data.get('response', {}).get('code')})")
                return False
        except Exception as e:
            logger.error(f"Videa login request error: {e}")
            return False

    def _get_upload_parameters(self, session: requests.Session) -> Dict[str, Any]:
        """Loads https://videa.hu/feltoltes and extracts upload_url and upload_token."""
        logger.info("Videa feltöltési oldal (https://videa.hu/feltoltes) lekérése...")
        resp = session.get("https://videa.hu/feltoltes", timeout=DEFAULT_TIMEOUT)
        logger.info(f"Videa /feltoltes válasz: HTTP {resp.status_code}, URL: {resp.url}, Méret: {len(resp.text)} byte")

        if resp.status_code != 200:
            logger.error(f"Videa /feltoltes hibás HTTP válasz: {resp.status_code}, URL: {resp.url}, Válasz minta: {resp.text[:300]}")
            raise ValueError(f"A Videa feltöltési oldal nem érhető el (HTTP {resp.status_code}).")

        if "/belepes" in resp.url or "form-login" in resp.text:
            logger.error(f"Videa átirányítás a belépési oldalra: {resp.url} - A fiók munkamenete lejárt vagy nincs bejelentkezve!")
            raise PermissionError("Videa session is not authenticated on /feltoltes.")

        # Extract upload parameters from script
        # Example: page = new VIDEA.pages.upload({"maxfilesize":..., "upload_url":"...", "upload_token":"..."})
        match = re.search(r'new\s+VIDEA\.pages\.upload\s*\(\s*(\{.*?\})\s*\);', resp.text, re.DOTALL)
        if match:
            try:
                params = json.loads(match.group(1))
                url = params.get("upload_url", "")
                if url.startswith("//"):
                    url = "https:" + url
                elif url.startswith("/"):
                    url = "https://videa.hu" + url
                params["upload_url"] = url
                token = params.get("upload_token")
                if url and token:
                    logger.info(f"Videa feltöltési paraméterek sikeresen kinyerve (JSON): upload_url={url}, upload_token={token[:6]}...")
                    return params
                else:
                    logger.warning(f"Videa upload params JSON hiányos: {params}")
            except Exception as e:
                logger.error(f"Failed to parse upload params JSON: {e}")

        # Fallback regex search
        upload_url_m = re.search(r'["\']upload_url["\']\s*:\s*["\']([^"\']+)["\']', resp.text)
        upload_token_m = re.search(r'["\']upload_token["\']\s*:\s*["\']([^"\']+)["\']', resp.text)

        if upload_url_m and upload_token_m:
            url = upload_url_m.group(1).replace(r"\/", "/")
            if url.startswith("//"):
                url = "https:" + url
            elif url.startswith("/"):
                url = "https://videa.hu" + url
            token = upload_token_m.group(1)
            logger.info(f"Videa feltöltési paraméterek sikeresen kinyerve (Regex fallback): upload_url={url}, upload_token={token[:6]}...")
            return {
                "upload_url": url,
                "upload_token": token,
            }

        logger.error(
            f"Nem sikerült kinyerni az upload_url és upload_token paramétereket a Videa feltöltési oldaláról! "
            f"HTTP: {resp.status_code}, URL: {resp.url}, HTML minta (első 1500 karakter):\n{resp.text[:1500]}"
        )
        raise ValueError(
            f"Nem sikerült kinyerni az upload_url és upload_token paramétereket a Videa feltöltési oldaláról. (HTTP {resp.status_code}, URL: {resp.url})"
        )

    def upload(
        self,
        file_path: str,
        title: str,
        description: str = "",
        account: Optional[Dict[str, Any]] = None,
        progress_callback: Optional[Callable[[float], None]] = None,
        cancel_event: Optional[threading.Event] = None,
        **kwargs,
    ) -> UploadResult:
        """
        Uploads a video to Videa.hu with 'nem listázott' (unlisted) visibility.
        """
        path = Path(file_path)
        if not path.is_file():
            raise FileNotFoundError(f"Video fájl nem található: {file_path}")

        file_size = path.stat().st_size
        filename = path.name

        session = self._create_session(account)

        # 1. Ensure authenticated using the same active session
        if not self.validate_credentials(account, session=session):
            raise PermissionError(f"Videa fiók hitelesítése sikertelen: {account.get('name') if account else 'Nincs megadva fiók'}")

        # 2. Get upload URL & token
        upload_params = self._get_upload_parameters(session)
        upload_url = upload_params["upload_url"]
        if upload_url.startswith("//"):
            upload_url = "https:" + upload_url
        elif upload_url.startswith("/"):
            upload_url = "https://videa.hu" + upload_url
        upload_token = upload_params["upload_token"]

        logger.info(f"Uploading {filename} ({file_size} bytes) to Videa via {upload_url}")
        if progress_callback:
            progress_callback(5.0)

        # 3. Stream video file directly from disk (zero-RAM buffering)
        mime_type, _ = mimetypes.guess_type(filename)
        mime_type = mime_type or "video/mp4"
        sid_cookie = session.cookies.get("sid", "")

        # Chunk size setting for Videa (default: 16 MB)
        videa_chunk_mb_setting = get_setting("videa_chunk_size_mb")
        try:
            videa_chunk_mb = int(videa_chunk_mb_setting) if videa_chunk_mb_setting else DEFAULT_VIDEA_CHUNK_SIZE_MB
        except Exception:
            videa_chunk_mb = DEFAULT_VIDEA_CHUNK_SIZE_MB
        chunk_size_bytes = max(1, videa_chunk_mb) * 1024 * 1024

        fields = {
            "token": upload_token,
            "sid": sid_cookie,
            "type": "video",
            "cmd": "uploadVideoFile",
        }

        stream = DiskStreamingMultipart(
            fields=fields,
            file_field_name="uploadfile",
            file_path=path,
            filename=filename,
            mime_type=mime_type,
            chunk_size=chunk_size_bytes,
            progress_callback=progress_callback,
            cancel_event=cancel_event,
        )

        try:
            resp = session.post(
                upload_url,
                data=stream,
                headers=stream.headers,
                timeout=14400,  # 4 hours timeout for large files
            )
            resp.raise_for_status()
            upload_response = resp.json()
        finally:
            stream.close()

        if upload_response.get("response", {}).get("code") != 0:
            err_code = upload_response.get("response", {}).get("code")
            raise RuntimeError(f"Videa fájl feltöltési hiba, kód: {err_code}")

        tmp_video_file = upload_response["response"]
        vcode = tmp_video_file.get("vcode")
        if not vcode:
            raise ValueError(f"Nem érkezett vcode a feltöltési válaszban: {upload_response}")

        logger.info(f"File uploaded, server vcode: {vcode}. Finalizing video details...")
        if progress_callback:
            progress_callback(90.0)

        # 4. Finalize video metadata via shareThisByRequest
        # mode: 2 = "nem listázott" (unlisted) as explicitly specified
        post_json = {
            "cmd": "shareThisByRequest",
            "title": title or filename,
            "description": description or title or filename,
            "item_content_rating": 0,  # 0 = Normál
            "drm": 0,
            "mode": 2,  # 2 = NEM LISTÁZOTT / UNLISTED
            "podcast": 0,
            "category": 13,  # 13 = Film és animáció
            "tags": ["film", "sorozat"],
            "copyright": "on",
            "is_disturbing_content": 0,
            "paidpromotion": 0,
            "subtitles": [],
            "file": tmp_video_file,
        }

        finalize_resp = session.post(
            "https://videa.hu/interface?logcmd=shareThisByRequest",
            data={"json": json.dumps(post_json)},
            headers={
                "Referer": "https://videa.hu/feltoltes",
                "X-Requested-With": "XMLHttpRequest",
                "Content-Type": "application/x-www-form-urlencoded; charset=utf-8",
            },
            timeout=DEFAULT_TIMEOUT,
        )
        finalize_data = finalize_resp.json()
        if finalize_data.get("response", {}).get("code") != 0:
            err = finalize_data.get("response", {}).get("code")
            logger.warning(f"Videa finalization warning (code {err}): {finalize_data}")

        direct_url = f"https://videa.hu/videok/{vcode}"
        embed_url = f"https://videa.hu/player?v={vcode}"
        logger.info(f"Videa file uploaded and details finalized! Direct link: {direct_url}, vcode: {vcode}")

        if progress_callback:
            progress_callback(100.0)

        return UploadResult(
            direct_url=direct_url,
            embed_url=embed_url,
            provider_video_id=vcode,
            raw_data={"upload": upload_response, "finalize": finalize_data, "session": session},
        )

    def check_conversion_status(
        self,
        account: Optional[Dict[str, Any]],
        vcode: str,
        session: Optional[requests.Session] = None,
    ) -> bool:
        """
        Performs a single, non-blocking check to determine if Videa transcoding is finished.
        Returns True if finished, False if still converting or unknown.
        """
        if not session:
            session = self._create_session(account)
            if account and not self.validate_credentials(account):
                self.login(account, session=session)

        try:
            resp = session.post(
                "https://videa.hu/interface?logcmd=getprogress",
                data={"json": json.dumps({"cmd": "getProgressByRequest"})},
                headers={
                    "Referer": "https://videa.hu/videoim",
                    "X-Requested-With": "XMLHttpRequest",
                    "Content-Type": "application/x-www-form-urlencoded; charset=utf-8",
                },
                timeout=5,
            )
            data = resp.json() if resp.status_code == 200 else {}
            progresses = data.get("response", {}).get("progresses", [])
            v_prog = next((p for p in progresses if p.get("id") == vcode), None)
            if v_prog:
                return bool(v_prog.get("finished") is True)
            else:
                # Video not in active converting list: check if player page actually has playable stream!
                # Do NOT just check status_code == 200, because Videa returns 200 even for videos being converted.
                player_resp = session.get(f"https://videa.hu/player?v={vcode}", timeout=5)
                if player_resp.status_code == 200:
                    text_lower = player_resp.text.lower()
                    if "feldolgoz" in text_lower or "átalakítás" in text_lower or "transcoding" in text_lower:
                        return False
                    if "video_sources" in text_lower or "sources:" in text_lower or ".mp4" in text_lower:
                        return True
                return False
        except Exception as e:
            logger.warning(f"Error in quick check_conversion_status for {vcode}: {e}")
            return False

    def wait_for_conversion(
        self,
        account: Optional[Dict[str, Any]],
        vcode: str,
        progress_callback: Optional[Callable[[float, str], None]] = None,
        cancel_event: Optional[threading.Event] = None,
        max_wait_seconds: int = 7200,
        poll_interval: float = 5.0,
        session: Optional[requests.Session] = None,
    ) -> bool:
        """
        Polls Videa until video transcoding is completely finished.
        Uses POST /interface?logcmd=getprogress with {"cmd": "getProgressByRequest"}.
        """
        if not session:
            session = self._create_session(account)
            if not self.validate_credentials(account):
                self.login(account, session=session)

        logger.info(f"Waiting for Videa transcoding to complete for video {vcode}...")
        start_time_wait = time.time()
        seen_in_progress = False

        while (time.time() - start_time_wait) < max_wait_seconds:
            if cancel_event and cancel_event.is_set():
                raise UploadCancelledException("Feltöltés megszakítva a felhasználó által.")

            try:
                resp = session.post(
                    "https://videa.hu/interface?logcmd=getprogress",
                    data={"json": json.dumps({"cmd": "getProgressByRequest"})},
                    headers={
                        "Referer": "https://videa.hu/videoim",
                        "X-Requested-With": "XMLHttpRequest",
                        "Content-Type": "application/x-www-form-urlencoded; charset=utf-8",
                    },
                    timeout=15,
                )
                data = resp.json() if resp.status_code == 200 else {}
                code = data.get("response", {}).get("code")

                # If session expired, re-login
                if code != 0:
                    logger.info("Videa getProgress returned non-zero code, refreshing session login...")
                    if account:
                        self.login(account, session=session)
                    time.sleep(2)
                    continue

                progresses = data.get("response", {}).get("progresses", [])
                v_prog = next((p for p in progresses if p.get("id") == vcode), None)

                if v_prog:
                    seen_in_progress = True
                    if v_prog.get("finished") is True:
                        logger.info(f"Videa video {vcode} marked finished: true in getProgress!")
                        if progress_callback:
                            progress_callback(100.0, "Videa konvertálás befejezve (100%)")
                        time.sleep(3)
                        return True

                    # Calculate progress percentage
                    s_time = v_prog.get("server_time", time.time())
                    p_start = v_prog.get("start_time", 0)
                    p_end = v_prog.get("end_time", 0)
                    total_dur = p_end - p_start
                    if total_dur > 0:
                        elapsed = s_time - p_start
                        pct = max(1.0, min(99.0, (elapsed / total_dur) * 100.0))
                    else:
                        pct = 50.0

                    logger.info(f"Videa transcoding {vcode}: {pct:.1f}%")
                    if progress_callback:
                        progress_callback(pct, f"Videa konvertálás folyamatban ({pct:.1f}%)...")
                else:
                    # Video not found in current progresses list
                    # It could be that it finished and dropped, OR it is still queued/waiting.
                    # Verify if player actually has playable video stream, NOT just HTTP 200!
                    is_ready = False
                    try:
                        player_chk = session.get(f"https://videa.hu/player?v={vcode}", timeout=5)
                        if player_chk.status_code == 200:
                            chk_lower = player_chk.text.lower()
                            if not ("feldolgoz" in chk_lower or "átalakítás" in chk_lower or "transcoding" in chk_lower):
                                if "video_sources" in chk_lower or "sources:" in chk_lower or ".mp4" in chk_lower:
                                    is_ready = True
                    except Exception:
                        pass

                    if is_ready:
                        logger.info(f"Videa player verified ready with video stream for {vcode}. Transcoding complete!")
                        if progress_callback:
                            progress_callback(100.0, "Videa konvertálás befejezve (100%)")
                        time.sleep(1)
                        return True

                    elapsed_wait = time.time() - start_time_wait
                    if seen_in_progress and elapsed_wait > 30:
                        # Video was previously seen in progress and has now completed and dropped
                        logger.info(f"Videa video {vcode} finished transcoding and left conversion queue.")
                        if progress_callback:
                            progress_callback(100.0, "Videa konvertálás befejezve (100%)")
                        time.sleep(1)
                        return True
                    else:
                        if progress_callback:
                            progress_callback(5.0, "Várakozás a Videa átalakító sorára...")

            except Exception as e:
                logger.warning(f"Error checking Videa conversion status for {vcode}: {e}")

            if cancel_event:
                if cancel_event.wait(poll_interval):
                    raise UploadCancelledException("Feltöltés megszakítva a felhasználó által.")
            else:
                time.sleep(poll_interval)

        raise TimeoutError(f"Időtúllépés: A Videa videó ({vcode}) konvertálása nem fejeződött be {max_wait_seconds} másodpercen belül.")
