import os
import re
import json
import hmac
import hashlib
import base64
import time as _time
from urllib.parse import urlparse, urljoin, quote, unquote

import requests
from flask import Flask, request, Response, jsonify

BASE_DIR = os.path.dirname(os.path.abspath(__file__))


def _load_dotenv():
    """Le o .env da VPS sem depender de python-dotenv.

    O painel Pterodactyl desta VPS nao tem o campo de variaveis de ambiente,
    entao a configuracao fica em arquivo. Prioridade: variavel real do
    sistema > .env. Assim quem seta no painel ainda ganha, e o .env serve de
    padrao/editavel pelo File Manager.
    """
    path = os.path.join(BASE_DIR, '.env')
    if not os.path.exists(path):
        return
    try:
        with open(path, encoding='utf-8') as fh:
            for raw in fh:
                line = raw.strip()
                if not line or line.startswith('#'):
                    continue
                if line.startswith('export '):
                    line = line[7:].strip()
                if '=' not in line:
                    continue
                key, _, value = line.partition('=')
                key = key.strip()
                value = value.strip()
                # remove aspas envolventes, se houver
                if len(value) >= 2 and value[0] == value[-1] and value[0] in ('"', "'"):
                    value = value[1:-1]
                # NAO sobrescreve o que ja vier do ambiente (SERVER_PORT etc.)
                os.environ.setdefault(key, value)
    except Exception as exc:
        print(f'[stream] falha ao ler .env: {exc}', flush=True)


_load_dotenv()

app = Flask(__name__)

STREAM_TOKEN_EXPIRY = int(os.environ.get('STREAM_TOKEN_EXPIRY', '300'))  # 5 min default p/ handoff VPS
SECRET_KEY = os.environ.get('STREAM_PROXY_SECRET') or os.environ.get('SECRET_KEY') or 'troco-play-stream-secret'
PROXY_CONNECT_TIMEOUT = max(2, int(os.environ.get('PROXY_CONNECT_TIMEOUT', '5') or 5))
PROXY_READ_TIMEOUT = max(5, int(os.environ.get('PROXY_READ_TIMEOUT', '20') or 20))
PROXY_UPSTREAM_TIMEOUT = (PROXY_CONNECT_TIMEOUT, PROXY_READ_TIMEOUT)
PROXY_MAX_STREAM_SECONDS = max(300, int(os.environ.get('PROXY_MAX_STREAM_SECONDS', '10800') or 10800))  # 3h

if SECRET_KEY == 'troco-play-stream-secret':
    print('[stream] AVISO: STREAM_PROXY_SECRET nao definida (usando segredo padrao inseguro).', flush=True)


def _sign_token(payload_b64: str) -> str:
    return hmac.new(SECRET_KEY.encode('utf-8'), payload_b64.encode('utf-8'), hashlib.sha256).hexdigest()


def _verify_stream_token(token: str, user_id=None):
    if not token:
        return None
    try:
        payload_b64, sig = token.rsplit('.', 1)
    except ValueError:
        return None
    try:
        expected = _sign_token(payload_b64)
        if not hmac.compare_digest(sig, expected):
            return None
        payload_bytes = base64.urlsafe_b64decode(payload_b64.encode('utf-8'))
        payload = json.loads(payload_bytes.decode('utf-8'))
    except Exception:
        return None
    if _time.time() > payload.get('exp', 0):
        return None
    if user_id is not None and payload.get('uid') != user_id:
        return None
    return payload


@app.route('/health', methods=['GET'])
def health():
    return jsonify({'status': 'ok', 'ts': _time.time()})


@app.route('/stream', methods=['GET'])
def stream():
    token = request.args.get('token', '')
    payload = _verify_stream_token(token)
    if not payload:
        return 'Token de acesso inválido ou expirado.', 403

    upstream_url = payload.get('url') or ''
    if not upstream_url:
        return 'URL de origem ausente no token.', 400

    try:
        parsed = urlparse(upstream_url)
        if parsed.scheme not in ('http', 'https'):
            return 'Esquema inválido.', 400
    except Exception:
        return 'URL inválida.', 400

    headers = {
        'User-Agent': payload.get('user_agent') or os.environ.get('STREAM_USER_AGENT', 'VLC/3.0'),
        'Accept': '*/*',
        'Accept-Language': 'pt-BR,pt;q=0.9,en-US;q=0.8,en;q=0.7',
        'Connection': 'keep-alive',
    }
    referrer = payload.get('referrer') or os.environ.get('STREAM_REFERER', '')
    if referrer:
        headers['Referer'] = referrer
        try:
            ru = urlparse(referrer)
            if ru.scheme and ru.netloc:
                headers['Origin'] = f'{ru.scheme}://{ru.netloc}'
        except Exception:
            pass

    for hname in ('Range', 'If-None-Match', 'If-Modified-Since', 'If-Range'):
        if request.headers.get(hname):
            headers[hname] = request.headers[hname]

    r = None
    try:
        r = requests.get(upstream_url, headers=headers, timeout=PROXY_UPSTREAM_TIMEOUT, stream=True)
    except requests.RequestException as exc:
        return f'Erro de conexão com origem: {exc}', 502

    excluded_resp = {'content-length', 'transfer-encoding', 'content-encoding', 'connection'}
    resp_headers = {k: v for k, v in r.headers.items() if k.lower() not in excluded_resp}

    content_type = r.headers.get('Content-Type', 'application/octet-stream')
    url_lower = upstream_url.lower()

    is_m3u8 = (
        'mpegurl' in content_type.lower() or
        url_lower.endswith('.m3u8') or
        '.m3u8?' in url_lower or
        url_lower.endswith('.m3u8/')
    )

    if is_m3u8:
        try:
            body = r.text
        except Exception:
            try:
                body = r.content.decode('utf-8', errors='replace')
            except Exception:
                body = ''

        if not body.strip() or ('#EXTM3U' not in body and '#EXT' not in body):
            return Response(r.content, status=r.status_code, content_type=content_type, headers=resp_headers)

        final_url = r.url if r.url else upstream_url
        try:
            base_url = final_url.rsplit('/', 1)[0] + '/'
        except Exception:
            base_url = final_url + ('/' if not final_url.endswith('/') else '')

        def make_vps_url(target_url: str) -> str:
            try:
                child_payload = {
                    'url': target_url,
                    'uid': payload.get('uid'),
                    'referrer': payload.get('referrer') or '',
                    'user_agent': payload.get('user_agent') or '',
                    'source_index': payload.get('source_index'),
                    'exp': _time.time() + STREAM_TOKEN_EXPIRY,
                }
                pb = base64.urlsafe_b64encode(json.dumps(child_payload, separators=(',', ':')).encode('utf-8')).decode('utf-8')
                tchild = pb + '.' + _sign_token(pb)
                return '/stream?token=' + quote(tchild, safe='')
            except Exception:
                return '/stream?token=' + quote(token, safe='')

        def rewrite_line(line: str) -> str:
            s = line.strip()
            if not s or s.startswith('#'):
                if 'URI="' in s:
                    m = re.search(r'URI="([^"]+)"', s)
                    if m:
                        uri = m.group(1)
                        if not (uri.startswith('http://') or uri.startswith('https://')):
                            uri = urljoin(base_url, uri)
                        else:
                            try:
                                uri = unquote(uri)
                            except Exception:
                                pass
                        s = s[:m.start(1)] + make_vps_url(uri) + s[m.end(1):]
                return s
            if s.startswith('http://') or s.startswith('https://'):
                try:
                    su = unquote(s)
                except Exception:
                    su = s
                return make_vps_url(su)
            resolved = urljoin(base_url, s)
            return make_vps_url(resolved)

        rewritten = '\n'.join(rewrite_line(l) for l in body.splitlines())
        resp_headers['Content-Type'] = 'application/vnd.apple.mpegurl'
        resp_headers['Access-Control-Allow-Origin'] = '*'
        resp_headers['Cache-Control'] = 'no-store'
        resp_headers['X-Accel-Buffering'] = 'no'
        try:
            r.close()
        except Exception:
            pass
        return Response(rewritten, status=200, content_type='application/vnd.apple.mpegurl', headers=resp_headers)

    resp_headers['Access-Control-Allow-Origin'] = '*'
    resp_headers['Access-Control-Allow-Headers'] = 'Range, If-None-Match, If-Modified-Since, If-Range'
    resp_headers['X-Accel-Buffering'] = 'no'
    resp_headers['Cache-Control'] = 'no-store'

    def generate():
        started = _time.time()
        try:
            for chunk in r.iter_content(chunk_size=262144):
                if not chunk:
                    continue
                yield chunk
                if _time.time() - started > PROXY_MAX_STREAM_SECONDS:
                    break
        except GeneratorExit:
            pass
        except Exception:
            pass
        finally:
            try:
                r.close()
            except Exception:
                pass

    return Response(generate(), status=r.status_code, content_type=content_type, headers=resp_headers)


if __name__ == '__main__':
    # Pterodactyl injeta SERVER_PORT a cada start. NUNCA escrever a porta no
    # codigo: ela muda quando o usuario troca a porta principal no painel.
    porta = int(os.environ['SERVER_PORT'])
    app.run(host='0.0.0.0', port=porta, threaded=True)
