<?php
namespace app\admin\controller;

use think\Db;

class M3u8fix extends Base
{
    public function create()
    {
        try {
            if (!request()->isPost()) {
                return json(['code' => 0, 'msg' => 'Method Not Allowed']);
            }

            // === 入参 ===
            $userId     = input('post.userId/d', 0);
            $panType    = input('post.panType/s', 'alist');
            $rootPath   = input('post.root_path/s', '');
            $path       = input('post.path/s', '');
            $parentName = input('post.parentName/s', '');
            $parentIdIn = input('post.parentId/s', '');

            if ($userId <= 0) return json(['code' => 0, 'msg' => '参数缺失：userId']);
            if (strtolower($panType) !== 'alist') return json(['code' => 0, 'msg' => '仅支持 alist']);
            if ($parentName === '') return json(['code' => 0, 'msg' => '参数缺失：parentName']);

            // === AList 配置 ===
            $alist = config('maopan.alist');

            // api_url：用于 /api/fs/get
            $apiBase = rtrim($alist['api_url'] ?? '', '/');

            // download_url：用于 /d/...（建议显式配置为 https://ol.feikuai.tv）
            $downloadBase = rtrim($alist['download_url'] ?? '', '/');
            if ($downloadBase === '') {
                // 兜底：从 apiBase 推导，去掉末尾 /api
                $downloadBase = preg_replace('#/api/?$#', '', $apiBase);
            }

            $token = $alist['token'] ?? '';
            if ($apiBase === '' || $token === '') {
                return json(['code' => 0, 'msg' => 'Alist 配置缺失：maopan.alist.api_url / maopan.alist.token']);
            }
            if ($downloadBase === '') {
                return json(['code' => 0, 'msg' => 'Alist 配置缺失：maopan.alist.download_url（或无法从 api_url 推导）']);
            }

            // === 拼目录路径（AList 内路径）===
            $dirPath = $this->joinPath($rootPath, $path);

            // parentId：优先用前端传入的 32位 md5，否则 md5(目录路径)
            $parentId = preg_match('/^[a-f0-9]{32}$/i', $parentIdIn) ? strtolower($parentIdIn) : md5($dirPath);

            // === playlist 路径 ===
            $playlistPath = $this->joinPath($dirPath, '/playlist.m3u8');

            // 1) 用 fs/get 确认存在 + 拿 raw_url
            $fsGet = $this->alistFsGet($apiBase, $token, $playlistPath);
            if ($fsGet['ok'] !== true) {
                return json([
                    'code' => 0,
                    'msg' => '未找到 playlist.m3u8：' . $fsGet['msg'],
                    'playlistPath' => $playlistPath
                ]);
            }

            $rawUrl = (string)($fsGet['raw_url'] ?? '');
            if ($rawUrl === '') {
                return json(['code' => 0, 'msg' => 'fs/get 未返回 raw_url，无法拉取 playlist 内容', 'playlistPath' => $playlistPath]);
            }

            // 2) 生成你库里一致的 /d/.../playlist.m3u8（注意：这是写库用的）
            //    你抓包的“正常播放”就是从 ol.feikuai.tv/d/.../000.xxx 302 到网盘直链。
            $dUrl = $downloadBase . '/d' . $this->encodePathForD($playlistPath);

            // 3) 拉 playlist 内容：优先 raw_url（更容易抓），失败再试 /d
            $r1 = $this->httpGetEx($rawUrl);
            $origin = $r1['ok'] ? $r1['body'] : '';

            $r2 = null;
            if ($origin === '') {
                $r2 = $this->httpGetEx($dUrl);
                if ($r2['ok']) $origin = $r2['body'];
            }

            if ($origin === '') {
                return json([
                    'code' => 0,
                    'msg'  => '无法获取 playlist.m3u8 内容（raw_url 与 /d 均失败）',
                    'raw_url' => $rawUrl,
                    'raw_http' => $r1['code'],
                    'd_url' => $dUrl,
                    'd_http' => $r2 ? $r2['code'] : null,
                ]);
            }

            // 4) 生成 m3u8Info：严格以 AList 的 playlist.m3u8 为准，原封不动保留分片文件名（扩展名不固定）
            //    不要再把分片改写成 000.ai，否则会导致你看到的“第二跳回到 jx 域名”或拼接异常。
            $m3u8Info = $this->normalizeM3u8($origin);

            // === 写库：严格对齐 think_m3u8.sql 字段 ===
            $now = date('Y-m-d H:i:s');
            $data = [
                'userId'     => $userId,
                'panType'    => 'alist',
                'fileId'     => null,
                'parentId'   => $parentId,
                'parentName' => $parentName,
                'm3u8file'   => 'playlist',
                'm3u8Info'   => $m3u8Info,
                // ✅ 写库必须用 /d/.../playlist.m3u8（复刻你库里可播放记录）
                'm3u8Link'   => $dUrl,
                'data_lock'  => 0,
                'visits'     => 0,
                'add_time'   => $now,
            ];

            $existsId = Db::name('m3u8')->where('parentId', $parentId)->value('id');
            $site = request()->domain();

            // ✅ 已生成禁止重复生成：只能删除旧链接后再重新生成
            if ($existsId) {
                return json([
                    'code' => 0,
                    'msg'  => '已生成，禁止重复生成！请先删除旧链接后再重新生成。',
                    'exists' => 1,
                    'id' => (int)$existsId,
                    'parentId' => $parentId,
                    // 返回已有链接，方便前端提示/复制（不算重新生成）
                    'm3u8' => $site . "/m3u8/alist/{$parentId}/playlist.m3u8",
                    'play' => $site . "/video/alist/{$parentId}",
                    'm3u8Link' => $dUrl,
                ]);
            }

            $id = (int)Db::name('m3u8')->insertGetId($data);

            return json([
                'code' => 1,
                'msg'  => '创建成功',
                'id'   => $id,
                'parentId' => $parentId,
                'm3u8' => $site . "/m3u8/alist/{$parentId}/playlist.m3u8",
                'play' => $site . "/video/alist/{$parentId}",
                'm3u8Link' => $dUrl,
            ]);

        } catch (\Throwable $e) {
            return json([
                'code' => 0,
                'msg'  => '服务器异常：' . $e->getMessage(),
                'file' => $e->getFile(),
                'line' => $e->getLine(),
            ]);
        }
    }

    /**
     * 规范化 m3u8 内容（不改分片文件名/扩展名，只做换行与必要的头尾补全）
     */
    private function normalizeM3u8($m3u8)
    {
        $m3u8 = (string)$m3u8;
        // 统一换行
        $m3u8 = str_replace(["\r\n", "\r"], "\n", $m3u8);
        // 去 BOM
        $m3u8 = preg_replace('/^\xEF\xBB\xBF/', '', $m3u8);
        // 去掉首尾空行（不要 trim 每一行，避免破坏 URI）
        $m3u8 = trim($m3u8);

        // 确保以 #EXTM3U 开头
        if ($m3u8 !== '' && stripos($m3u8, '#EXTM3U') !== 0) {
            $m3u8 = "#EXTM3U\n" . $m3u8;
        }
        // 点播列表一般应有 ENDLIST；若缺失则补上
        if ($m3u8 !== '' && stripos($m3u8, '#EXT-X-ENDLIST') === false) {
            $m3u8 .= "\n#EXT-X-ENDLIST";
        }
        return $m3u8 . "\n";
    }

    private function alistFsGet($apiBase, $token, $path)
    {
        $url = $this->apiUrl($apiBase, 'fs/get');
        $payload = ['path' => $path, 'password' => ''];

        $res = $this->httpPostJson($url, $payload, $token);
        if ($res['ok'] !== true) return ['ok' => false, 'msg' => $res['msg']];

        $j = $res['json'];
        if (!is_array($j) || (int)($j['code'] ?? 0) !== 200) {
            return ['ok' => false, 'msg' => 'fs/get 返回异常'];
        }
        return ['ok' => true, 'raw_url' => ($j['data']['raw_url'] ?? '')];
    }

    private function apiUrl($apiBase, $suffix)
    {
        // 兼容 api_url 可能是 https://xx 或 https://xx/api
        $apiBase = rtrim($apiBase, '/');
        if (preg_match('#/api$#', $apiBase)) {
            return $apiBase . '/' . ltrim($suffix, '/');
        }
        return $apiBase . '/api/' . ltrim($suffix, '/');
    }

    private function joinPath($a, $b)
    {
        $a = (string)$a; $b = (string)$b;
        if ($a === '' && $b === '') return '';
        if ($a === '') return $b;
        if ($b === '') return $a;
        return rtrim($a, '/') . '/' . ltrim($b, '/');
    }

    private function encodePathForD($path)
    {
        $path = (string)$path;
        $parts = explode('/', $path);
        $out = [];
        foreach ($parts as $i => $seg) {
            if ($i === 0 && $seg === '') continue;
            $out[] = rawurlencode($seg);
        }
        return '/' . implode('/', $out);
    }

    private function httpGetEx($url)
    {
        if (!function_exists('curl_init')) {
            $body = @file_get_contents($url);
            return ['ok' => is_string($body) && $body !== '', 'code' => 0, 'body' => (string)$body];
        }

        $ch = curl_init($url);
        curl_setopt_array($ch, [
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_FOLLOWLOCATION => true,
            CURLOPT_TIMEOUT => 20,
            CURLOPT_CONNECTTIMEOUT => 10,
            CURLOPT_SSL_VERIFYPEER => false,
            CURLOPT_SSL_VERIFYHOST => false,
            CURLOPT_USERAGENT => 'Mozilla/5.0',
        ]);
        $body = curl_exec($ch);
        $code = (int)curl_getinfo($ch, CURLINFO_HTTP_CODE);
        curl_close($ch);

        $ok = ($code >= 200 && $code < 300 && is_string($body) && $body !== '');
        return ['ok' => $ok, 'code' => $code, 'body' => $ok ? $body : ''];
    }

    private function httpPostJson($url, $payload, $token)
    {
        $raw = json_encode($payload, JSON_UNESCAPED_UNICODE);

        $ch = curl_init($url);
        curl_setopt_array($ch, [
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_POST => true,
            CURLOPT_POSTFIELDS => $raw,
            CURLOPT_HTTPHEADER => [
                'Content-Type: application/json',
                'Authorization: ' . $token,
            ],
            CURLOPT_TIMEOUT => 20,
            CURLOPT_CONNECTTIMEOUT => 10,
            CURLOPT_SSL_VERIFYPEER => false,
            CURLOPT_SSL_VERIFYHOST => false,
        ]);
        $body = curl_exec($ch);
        $err  = curl_error($ch);
        $code = (int)curl_getinfo($ch, CURLINFO_HTTP_CODE);
        curl_close($ch);

        if (!is_string($body) || $body === '' || $code < 200 || $code >= 300) {
            return ['ok' => false, 'msg' => "HTTP失败 {$code} {$err}"];
        }
        return ['ok' => true, 'json' => json_decode($body, true)];
    }
}
