import json
import os
import subprocess
import time
import urllib.parse

import xbmc
import xbmcaddon
import xbmcgui
import xbmcvfs

from tenbuttons_streams_multi import _handle_choice_multi, _handle_choice_multi_choose_source
from tenbuttons_streams_playback import (
    _handle_choice_android_external,
    _handle_choice_mpc,
    _handle_choice_play_mpv,
    _handle_choice_vlc,
    _show_directions_199,
)
from tenbuttons_streams_recording import (
    _handle_choice_record,
    _handle_choice_stop_recording,
    _handle_choice_watch_recording,
)

xbc = xbmc
xbcaddon = xbmcaddon
xbcgui = xbmcgui
xbcvfs = xbmcvfs


def _persist_resolved_streams(temp_file, resolved_streams):
    try:
        with xbcvfs.File(temp_file, "w") as f:
            f.write(json.dumps(resolved_streams, indent=2))
        xbc.log(f"DEBUG: Saved resolved streams to {temp_file}: {json.dumps(resolved_streams, indent=2)}", level=xbc.LOGINFO)
        return True
    except Exception as e:
        xbc.log(f"DEBUG: Failed to save resolved streams to {temp_file}: {str(e)}", level=xbc.LOGERROR)
        xbmcgui.Dialog().ok("Error", f"Failed to save resolved streams: {str(e)}")
        return False


def _launch_multi_streams_mpv(resolved_streams, mpv_path):
    if len(resolved_streams) < 2:
        xbmcgui.Dialog().ok("Error", "Fewer than two streams could be resolved. At least two valid streams are required.")
        return

    mpv_args = []
    all_headers = []
    for stream in resolved_streams:
        all_headers.extend([f"{k.title().replace('-', '-')}: {v}" for k, v in stream["headers"].items()])
    if all_headers:
        mpv_args += [f'--http-header-fields={",".join(set(all_headers))}']

    num_streams = len(resolved_streams)
    video_filters = []
    for i in range(num_streams):
        if i == 0:
            video_filters.append(f"[vid{num_streams}]scale=1280:720[v{i}];")
        else:
            video_filters.append(f"[vid{i}]scale=1280:720[v{i}];")
    if num_streams <= 4:
        video_filters.append("".join(f"[v{i}]" for i in range(num_streams)) + f"hstack=inputs={num_streams}[vo];")
    else:
        video_filters.append("".join(f"[v{i}]" for i in range(num_streams)) + f"xstack=inputs={num_streams}:layout=0_0|0_h0|w0_0|w0_h0[vo];")

    audio_titles = [stream["title"] for stream in resolved_streams]
    audio_choice = xbcgui.Dialog().select("Select which stream's audio to use", audio_titles)
    if audio_choice == -1:
        xbmcgui.Dialog().ok("Error", "No audio stream selected.")
        return

    audio_index = audio_choice + 1 if audio_choice > 0 else num_streams
    audio_filters = [f"[aid{audio_index}]anull[ao]"]
    lavfi_filter = "".join(video_filters) + "".join(audio_filters)
    mpv_args.append(f'--lavfi-complex={lavfi_filter}')

    loop_choice = xbcgui.Dialog().select("Enable Loop/Repeat?", ["No Loop (default)", "Loop Single Stream (--loop-file=inf)", "Loop All Streams (--loop-playlist=inf)"])
    if loop_choice == 1:
        mpv_args.append('--loop-file=inf')
    elif loop_choice == 2:
        mpv_args.append('--loop-playlist=inf')

    for i in range(1, num_streams):
        stream_url = resolved_streams[i]["url"]
        if "|" in stream_url:
            stream_url = stream_url.split("|", 1)[0]
        mpv_args.append(f'--external-file={stream_url}')

    first_stream_url = resolved_streams[0]["url"]
    if "|" in first_stream_url:
        first_stream_url = first_stream_url.split("|", 1)[0]
    mpv_command = [mpv_path] + mpv_args + [first_stream_url]
    xbc.log(f"DEBUG: Launching mpv with command: {mpv_command}", level=xbc.LOGINFO)

    create_new_console = 0x00000010
    try:
        proc = subprocess.Popen(mpv_command, creationflags=create_new_console, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
        xbmcgui.Dialog().notification("mpv", f"Starting {num_streams} streams in mpv...", xbmcgui.NOTIFICATION_INFO, 2000)
        time.sleep(2)
        if proc.poll() is None:
            xbc.log(f"DEBUG: mpv process started with PID: {proc.pid}", level=xbc.LOGINFO)
            xbmcgui.Dialog().notification("mpv", f"Playing {num_streams} streams in mpv", xbmcgui.NOTIFICATION_INFO, 2000)
        else:
            stdout, stderr = proc.communicate(timeout=5)
            xbc.log(f"DEBUG: mpv stdout: {stdout}", level=xbc.LOGINFO)
            xbc.log(f"DEBUG: mpv stderr: {stderr}", level=xbc.LOGINFO)
            raise subprocess.CalledProcessError(proc.returncode, mpv_command, stdout, stderr)
    except Exception as e:
        xbc.log(f"DEBUG: Caught exception launching mpv: {str(e)}", level=xbc.LOGERROR)
        time.sleep(2)
        if proc.poll() is None:
            xbc.log(f"DEBUG: mpv process is running despite exception, PID: {proc.pid}", level=xbc.LOGINFO)
            xbmcgui.Dialog().notification("mpv", f"Playing {num_streams} streams in mpv", xbcgui.NOTIFICATION_INFO, 2000)
        else:
            xbmcgui.Dialog().ok("Error", f"Failed to launch mpv: {str(e)}")
            xbmcgui.Dialog().notification("mpv", "Multi-stream playback failed. Trying to play streams individually.", xbmcgui.NOTIFICATION_WARNING, 2000)
            for stream in resolved_streams:
                mpv_args_single = []
                if stream["headers"]:
                    mpv_headers = ",".join([f"{k.title().replace('-', '-')}: {v}" for k, v in stream["headers"].items()])
                    mpv_args_single += [f'--http-header-fields={mpv_headers}']
                stream_url = stream["url"]
                if "|" in stream_url:
                    stream_url = stream_url.split("|", 1)[0]
                mpv_command_single = [mpv_path] + mpv_args_single + [stream_url]
                xbc.log(f"DEBUG: Launching mpv single stream with command: {mpv_command_single}", level=xbc.LOGINFO)
                try:
                    proc = subprocess.Popen(mpv_command_single, creationflags=create_new_console, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
                    time.sleep(1)
                    if proc.poll() is None:
                        xbc.log(f"DEBUG: mpv single stream process started with PID: {proc.pid}", level=xbc.LOGINFO)
                        xbmcgui.Dialog().notification("mpv", f"Trying single stream: {stream['title']}", xbmcgui.NOTIFICATION_INFO, 2000)
                        time.sleep(5)
                    else:
                        stdout, stderr = proc.communicate(timeout=5)
                        xbc.log(f"DEBUG: mpv single stream stdout: {stdout}", level=xbc.LOGINFO)
                        xbc.log(f"DEBUG: mpv single stream stderr: {stderr}", level=xbc.LOGINFO)
                        xbc.log(f"DEBUG: mpv single stream failed with exit code: {proc.returncode}", level=xbc.LOGERROR)
                        xbmcgui.Dialog().ok("Error", f"Failed to launch mpv single stream {stream['title']}: Exit code {proc.returncode}")
                except Exception as e:
                    xbc.log(f"DEBUG: Failed to launch mpv single stream {stream['url']}: {str(e)}", level=xbc.LOGERROR)
                    xbmcgui.Dialog().ok("Error", f"Failed to launch mpv single stream {stream['title']}: {str(e)}")


def _get_resolved_url(url):
    resolved_url = None
    resolved_headers = {}
    if url.startswith("plugin://"):
        try:
            xbc.executebuiltin('PlayerControl(Stop)')
            xbc.sleep(500)
            list_item = xbcgui.ListItem()
            list_item.setPath(url)
            player = xbc.Player()
            player.play(url, list_item)
            start_time = time.time()
            timeout = 15
            while time.time() - start_time < timeout and not player.isPlaying():
                xbc.sleep(100)
            if player.isPlaying():
                resolved_url = player.getPlayingFile()
                xbc.log(f"DEBUG: Resolved URL from player: {resolved_url}", level=xbc.LOGINFO)
                if not resolved_url or not str(resolved_url).startswith('http'):
                    xbc.log(f"DEBUG: Trying JSON-RPC fallback, current resolved_url: {resolved_url}", level=xbc.LOGINFO)
                    jsonrpc_request = {
                        'jsonrpc': '2.0',
                        'method': 'Player.GetItem',
                        'params': {'playerid': 1},
                        'id': 1
                    }
                    result = xbc.executeJSONRPC(json.dumps(jsonrpc_request))
                    xbc.log(f"DEBUG: JSON-RPC GetItem result: {result}", level=xbc.LOGINFO)
                    data = json.loads(result)
                    item = data.get('result', {}).get('item', {})
                    file_path = item.get('file', '')
                    xbc.log(f"DEBUG: JSON-RPC file_path: {file_path}", level=xbc.LOGINFO)
                    if file_path and file_path.startswith('http'):
                        resolved_url = file_path
                        xbc.log(f"DEBUG: Resolved URL from JSON-RPC: {resolved_url}", level=xbc.LOGINFO)
                player.stop()
                xbc.sleep(300)
            else:
                xbc.log(f"DEBUG: Player failed to start for URL: {url}", level=xbc.LOGWARNING)
        except Exception as e:
            xbc.log(f"DEBUG: Error resolving plugin URL {url}: {str(e)}", level=xbc.LOGERROR)
        if not resolved_url:
            try:
                parsed_url = urllib.parse.urlparse(url)
                query_params = urllib.parse.parse_qs(parsed_url.query)
                if 'urls' in query_params and query_params['urls'][0].startswith("direct://"):
                    resolved_url = query_params['urls'][0].replace("direct://", "")
                    xbc.log(f"DEBUG: Extracted direct URL from plugin: {resolved_url}", level=xbc.LOGINFO)
            except Exception as e:
                xbc.log(f"DEBUG: Error extracting direct URL from plugin: {str(e)}", level=xbc.LOGERROR)
    else:
        resolved_url = url
        xbc.log(f"DEBUG: Using provided URL as resolved: {resolved_url}", level=xbc.LOGINFO)

    # Extract headers if present
    if resolved_url and '|' in resolved_url:
        url_parts = resolved_url.split('|', 1)
        resolved_url = url_parts[0]
        try:
            # Kodi-style: Referer=...&Origin=...&User-Agent=...
            header_string = url_parts[1]
            for header in header_string.split('&'):
                if '=' in header:
                    key, value = header.split('=', 1)
                    resolved_headers[key.lower()] = urllib.parse.unquote(value)
        except Exception as e:
            xbc.log(f"DEBUG: Error parsing headers from URL: {str(e)}", level=xbc.LOGWARNING)

    return resolved_url, resolved_headers



def handle_stream_action_choice(choice):
    try:
        player = xbc.Player()
        stream_url = None
        try:
            stream_url = player.getPlayingFile()
        except Exception:
            stream_url = None

        is_android = "ANDROID_DATA" in os.environ or "ANDROID_ROOT" in os.environ
        if is_android:
            ffmpeg_path = xbcaddon.Addon().getSetting("android_mpv_path") or "/data/data/com.termux/files/usr/bin/mpv"
            mpv_path = xbcaddon.Addon().getSetting("android_mpv_path") or "/data/data/com.termux/files/usr/bin/mpv"
            default_recording_path = xbcaddon.Addon().getSetting("android_recording_path") or "/data/data/com.termux/files/home/recordings"
        else:
            ffmpeg_path = xbcaddon.Addon().getSetting("ffmpeg_path") or r"C:\ffmpeg\bin\ffmpeg.exe"
            mpv_path = xbcaddon.Addon().getSetting("mpv_path") or r"C:\Users\user\Desktop\mpv_player\mpv.exe"
            default_recording_path = xbcaddon.Addon().getSetting("recording_path") or r"C:\Users\home\Videos"

        if choice == -1:
            return True
        if choice == 9:
            _show_directions_199()
            return True

        if choice in [0, 1, 2, 3, 6, 7, 8]:
            if not stream_url or not (str(stream_url).startswith("http") or str(stream_url).endswith(".m3u8") or str(stream_url).startswith("plugin://")):
                xbc.log(f"DEBUG: Invalid or no current stream: {stream_url}", level=xbc.LOGERROR)
                xbmcgui.Dialog().ok("Error", "No valid stream is currently playing. Start a stream to use this option.")
                return True

        url = stream_url
        if choice == 0:
            _handle_choice_play_mpv(url, mpv_path, _get_resolved_url)
        elif choice == 1:
            _handle_choice_record(url, is_android, mpv_path, ffmpeg_path, default_recording_path)
        elif choice == 2:
            _handle_choice_multi(url, mpv_path, _get_resolved_url, _persist_resolved_streams, _launch_multi_streams_mpv)
        elif choice == 3:
            _handle_choice_multi_choose_source(url, mpv_path, _get_resolved_url, _persist_resolved_streams, _launch_multi_streams_mpv)
        elif choice == 4:
            _handle_choice_stop_recording()
        elif choice == 5:
            _handle_choice_watch_recording()
        elif choice == 6:
            _handle_choice_vlc(url, is_android, _get_resolved_url)
        elif choice == 7:
            _handle_choice_mpc(url, is_android, _get_resolved_url)
        elif choice == 8:
            _handle_choice_android_external(url, is_android, _get_resolved_url)
        return True
    except Exception as e:
        xbmcgui.Dialog().ok("Error", f"Error handling click: {str(e)}")
        return True


def handle_stream_actions(control_id):
    if control_id != 199:
        return False
    choice = xbcgui.Dialog().select(
        "Choose Action",
        [
            "Play Stream in mpv",
            "Record Stream (ffmpeg)",
            "Play Multiple Streams in mpv",
            "Play Multiple Streams (Choose Source)",
            "Stop Recording",
            "Watch Recording",
            "Play Stream in VLC",
            "Play Stream in MPC-HC",
            "Play Stream in Android External Player",
            "Directions"
        ]
    )
    return handle_stream_action_choice(choice)
