<?php

namespace App\Service;

use App\Exceptions\UserException;
use App\Jobs\DownloadImageJob;
use App\Jobs\SendImagineJob;
use App\Models\PromptTask;
use App\Models\Task;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Redis;
use Illuminate\Support\Str;

class TaskService
{
    public const TYPE_IMG_TO_IMG = 1;  // 带图片的
    public const TYPE_TEXT_TO_IMG = 2;  // 文字转图片

    public const GPT_prompt_num = 1;  // 指令数量

    public const STATUS_WAIT = 0;      // 等待
    public const STATUS_SUCCESS = 1;    // 成功
    public const STATUS_FAIL = 2;       // 失败


    public const TASK_TYPE_AUTO = 1;    // 自动化
    public const TASK_TYPE_API = 2; // API
    public const TASK_TYPE_STABLE = 3; // stable

    /**
     * @param array $data
     */
    public function validateSubmitData(array $data = [])
    {
        $type = $data['type'] ?? '';
        $prompt = $data['prompt'] ?? '';
        $promptImg = $data['prompt_img'] ?? [];
        $promptNum = $data['prompt_num'] ?? '';

        if (empty($type) || empty($prompt)) {
            throw new UserException(1, '任务类型不能为空');
        }
        # 生成指令数量只能在1-10之间
        if ($promptNum < 1 || $promptNum > 10) {
            throw new UserException(1, '生成指令数量只能在1-10之间');
        }

        # 判断类型
        if ($type == self::TYPE_IMG_TO_IMG) {
            # 图片转图片
            if (!count($promptImg)) {
                throw new UserException(1, '图片不能为空');
            }
        } elseif ($type == self::TYPE_TEXT_TO_IMG) {
            # 文字转图片
        } else {
            throw new UserException(1, '任务类型错误');
        }
    }

    /**
     * 提交任务
     *
     * @param int $user_id
     * @param array $data
     * @return array
     */
    public function submit(int $user_id = 0, array $data = [])
    {
        $this->validateSubmitData($data);
        $data['prompt'] = str_replace('\n', '', $data['prompt']);
        $task = $this->createTask($user_id, $data);
        $promptTask = $this->createPromptTask($user_id, $task->id, $data);
        $list = [
            'type' => (string)($data['type'] ?? ''),
            'prompt' => (string)($data['prompt'] ?? ''),
            'prompt_img' => json_encode($data['prompt_img'] ?? []),
            'user_id' => $user_id,
            'prompt_num' => self::GPT_prompt_num,
        ];
        $list['prompt_id'] = $promptTask->id;
        # 插入redis,这是自动化的任务
        Redis::rpush('midjourney_prompt', json_encode($list));
        # 返回任务id
        return [
            'task_id' => $task->id,
            'prompt_num' => self::GPT_prompt_num,
        ];
    }

    /**
     * 创建任务
     *
     * @param int $userId
     * @param array $data
     * @return Task
     */
    public function createTask(int $userId = 0, array $data = [])
    {
        $taskData = [
            'type' => (string)($data['type'] ?? ''),
            'prompt' => (string)($data['prompt'] ?? ''),
            'prompt_img' => json_encode($data['prompt_img'] ?? []),
            'user_id' => $userId,
            'prompt_num' => self::GPT_prompt_num,
        ];
        return Task::create($taskData);
    }

    /**
     * 创建提示任务
     *
     * @param int $userId
     * @param int $taskId
     * @param array $data
     * @return PromptTask
     */
    public function createPromptTask(int $userId = 0, int $taskId = 0, array $data = [])
    {
        $promptTask = [
            'prompt' => (string)($data['prompt'] ?? ''),
            'user_id' => $userId,
            'task_id' => $taskId,
            'prompt_img' => json_encode($data['prompt_img'] ?? []),
            'type' => (string)($data['type']),
            'callback' => config('common.prompt_callback'),
            'policy' => config('common.policy_callback'),
        ];
        return PromptTask::create($promptTask);
    }


    /**
     * api 提交任务
     *
     * @param int $userId
     * @param array $data
     * @return array
     */
    public function apiSubmit(int $userId = 0, array $data = [])
    {
        $this->validateSubmitData($data);
        $data['prompt'] = str_replace('\n', '', $data['prompt']);
        $task = $this->createTask($userId, $data);
        $promptTask = $this->createPromptTask($userId, $task->id, $data);
        $sendData = [
            'token' => config('common.imagine_token'),
            'guild_id' => config('common.imagine_guild_id'),
            'channel_id' => config('common.imagine_channel_id'),
            'prompt' => $task->prompt,
            'prompt_img' => $task->prompt_img,
            'prompt_id' => $promptTask->id,
            'task_id' => $task->id,
            'type' => $task->type,
        ];
        dispatch(new SendImagineJob($sendData));
        return [
            'task_id' => $task->id,
            'prompt_num' => $task->prompt_num
        ];
    }

    /**
     * stable 提交任务=-------
     */
    public function stableSubmit(int $userId = 0, array $data = [])
    {
        $this->validateSubmitData($data);
        $data['prompt'] = str_replace('\n', '', $data['prompt']);
        $task = $this->createTask($userId, $data);
        $promptTask = $this->createPromptTask($userId, $task->id, $data);
        $sendData = [
            'prompt' => $task->prompt,
            'prompt_img' => $task->prompt_img,
            'prompt_id' => $promptTask->id,
            'task_id' => $task->id,
            'type' => $task->type,
        ];
        # 提交redis
        $status = Redis::rpush('stable_task', json_encode($sendData));
        return [
            'task_id' => $task->id,
            'prompt_num' => $task->prompt_num
        ];
    }

    /**
     * stable 轮询获取stable任务
     */
    public function getStableTask(array $data = [])
    {
        # 获取redis任务
        $taskList = Redis::lpop('stable_task');
        return $taskList;
    }

    /**
     *  stableCallback 回调
     */
    public function stableCallback(array $data = [])
    {
        # 必须字段
        $task_id = $data['task_id'] ?? '';
        $prompt_id = $data['prompt_id'] ?? '';
        $result_img = $data['result_img'] ?? '';
        $message = $data['message'] ?? '';
        $status = $data['status'] ?? false;
        if (empty($task_id) || empty($prompt_id)) {
            throw new UserException(1, '参数错误');
        }
        if (!$status) {
            # 任务失败
            $task = PromptTask::find($prompt_id);
            $sendData = [
                'prompt' => $task->prompt,
                'prompt_img' => $task->prompt_img,
                'prompt_id' => $prompt_id,
                'task_id' => $task->id,
                'type' => $task->type,
            ];
            # 提交redis
            Redis::rpush('stable_task', json_encode($sendData));
        } elseif ($status) {
            # 任务成功
            $task = PromptTask::find($prompt_id);
            $task->result_img = $result_img;
            $task->result_img_status = self::STATUS_SUCCESS;
            $task->save();
        }
    }


    /**
     * redis队列插入--这是gpt生成指令的任务
     */
    public function setRedis($task_id, $data = [])
    {
        $data['callback'] = config('common . callback');
        $data['task_id'] = $task_id;
        Redis::rpush('gpt_text_prompt', json_encode($data));
    }


    /**
     * python-gpt任务回调
     */
    public function gpt_callback($data = [])
    {
        $credentials = Arr::only($data, ['prompt', 'user_id', 'task_id', 'prompt_img', 'result_img', 'type']);
        # 必须字段
        $type = $credentials['type'];
        $prompt = $credentials['prompt'] ?? '';
        $prompt = json_decode($prompt);
        $user_id = $credentials['user_id'];
        $task_id = $credentials['task_id'];
        # 可选字段
        $prompt_img = $credentials['prompt_img'] ?? '';
        $result_img = $credentials['result_img'] ?? '';
        # 判断必须字段
        if (empty($type) || empty($prompt) || empty($user_id) || empty($task_id)) {
            throw new UserException(1, 'gpt_callback--$user_id,$task_id,$prompt,$type参数错误');
        }
        # 判断$data是否为数组
        if (!is_array($prompt)) {
            throw new UserException(1, 'gpt_callback--$prompt参数错误');
        }
        # 循环
        foreach ($prompt as $item) {
            $list = [
                'prompt' => $item,
                'user_id' => $user_id,
                'task_id' => $task_id,
                'prompt_img' => $prompt_img,
                'result_img' => $result_img,
                'type' => $type,
                'callback' => config('common . prompt_callback'),
                'policy' => config('common . policy_callback'),
            ];
            $prompt_id = promptTask::query()->create($list)->id;
            $list['prompt_id'] = $prompt_id;
            # 插入redis,这是自动化的任务
            Redis::rpush('midjourney_prompt', json_encode($list));
        }
        return true;
    }


    /**
     * python - 指令完成-回调
     */
    public function prompt_callback($data = [])
    {
        $credentials = Arr::only($data, ['user_id', 'task_id', 'result_img', 'prompt_id', 'message']);
        # 必须字段
        $prompt_id = $credentials['prompt_id'] ?? '';
        $user_id = $credentials['user_id'] ?? '';
        $task_id = $credentials['task_id'] ?? '';
        $result_img = $credentials['result_img'] ?? '';
        $message = $credentials['message'] ?? '';
        # 判断必须字段
        if (empty($user_id) || empty($task_id) || empty($result_img) || empty($prompt_id)) {
            throw new UserException(1, '指令回调缺少参数');
        }
        # 如果存在message,则提交失败信息
        if ($message) {
            $res = PromptTask::query()->where('id', $prompt_id)->where('user_id', $user_id)
                ->where('task_id', $task_id)->first();
            if ($res->message) {
                $res->message = $res->message . ',' . $message;
            } else {
                $res->message = $message;
            }
            $res->save();
        } else {
            # 更新
            $res = PromptTask::query()->where('id', $prompt_id)->where('user_id', $user_id)
                ->where('task_id', $task_id)->update(['result_img' => $result_img,]);
            $list = [
                'user_id' => $user_id,
                'task_id' => $task_id,
                'prompt_id' => $prompt_id,
                'result_img' => $result_img,
                'callback' => config('common . img_download_callback'),
            ];
            # redis队列插入任务--转存图片
            Redis::rpush('discord_img_download', json_encode($list));
        }
        return true;
    }

    /**
     * python - 下载图片完成-回调
     */
    public function img_download_callback($data = [])
    {
        $credentials = Arr::only($data, ['prompt_id', 'result_img', 'message']);
        # 必须字段
        $prompt_id = $credentials['prompt_id'] ?? '';
        $message = $credentials['message'] ?? '';
        $result_img = $credentials['result_img'] ?? '';
        # 判断必须字段
        if (empty($prompt_id)) {
            throw new UserException(1, '参数错误');
        }
        # 获取任务
        $task = PromptTask::query()->where('id', $prompt_id)->first();
        if ($task) {
            # 图片不存在则不更新图片
            if ((empty($result_img) || $result_img == '') && $message) {
                # 消息字段
                $old_message = $task->message ?? '';
                $task->update([
                    'message' => $old_message . ',' . $message,
                ]);
            } else if ($result_img) {
                # 更新
                $task->update([
                    'result_img' => $result_img,
                    'result_img_status' => 1,
                ]);
            }
        }
        return true;
    }

    /**
     * 轮询接口
     */
    public function task_callback($user_id, $data = [])
    {
        $credentials = Arr::only($data, ['task_id']);
        # 必须字段
        $task_id = $credentials['task_id'];
        # 判断必须字段
        if (empty($task_id)) {
            throw new UserException(1, '缺少id');
        }

        # 查询所有
        $res = PromptTask::query()->where('user_id', $user_id)
            ->where('task_id', $task_id)
            ->where('result_img_status', 1)
            ->get();

        $list = [];
        foreach ($res as $item) {
            $list[] = [
                'img' => $item->result_img,
                'id' => $item->id,
                'split_img' => ''
            ];
        }
        return $list;
    }

    /**
     * 创建切割图片任务
     */
    public function create_split_img_task(int $user_id = 0, $data = [])
    {
        $credentials = Arr::only($data, ['prompt_id', 'cut_id', 'type']);
        # 必须字段
        $prompt_id = $credentials['prompt_id'];
        $cut_id = $credentials['cut_id'];
        $type = $credentials['type'];
        # 判断必须字段
        if (empty($prompt_id) || empty($cut_id)) {
            throw new UserException(1, '没有这个任务');
        }
        # 找到prompt_id
        $prompt = PromptTask::query()->where('id', $prompt_id)->first();
        if (empty($prompt)) {
            throw new UserException(1, '没有这个任务');
        }
        # 更新
        $prompt->update([
            'cut_id' => $cut_id,
            'cut_status' => self::STATUS_WAIT,
        ]);
        $downloadData = [
            'url' => $prompt->result_img,
            'name' => Str::uuid()->toString(),
            'index' => $cut_id,
            'prompt_id' => $prompt_id,
        ];
        dispatch(new DownloadImageJob($downloadData));
        return [
            'prompt_id' => $prompt_id,
        ];
    }

    /**
     * 图片切割回调
     */
    public function split_callback($data = [])
    {
        $credentials = Arr::only($data, ['cut_img', 'prompt_id']);
        $cut_img = $credentials['cut_img'] ?? '';
        $prompt_id = $credentials['prompt_id'] ?? '';
        if (empty($prompt_id) || empty($cut_img)) {
            throw new UserException(1, '缺少必须字段');
        }
        $res = PromptTask::query()->where('id', $prompt_id)->first();
        if ($res) {
            # 更新
            $res = PromptTask::query()->where('id', $prompt_id)->update([
                'cut_img' => $cut_img,
                'cut_status' => 1,
            ]);
        } else {
            throw new UserException(1, '没有这个任务');
        }
        return '';

    }

    /**
     * 轮询获取切割后的图片
     */
    public function IntervalSplitImg($data = [])
    {
        $credentials = Arr::only($data, ['prompt_id']);
        $prompt_id = $credentials['prompt_id'] ?? '';
        if (empty($prompt_id)) {
            throw new UserException(1, '没有这个任务');
        }
        # 查询
        $res = PromptTask::query()->where('id', $prompt_id)->first();
        if (empty($res)) {
            throw new UserException(1, '没有这个任务');
        }
        if ($res->cut_status == 1) {
            return [
                'cut_img' => $res->cut_img,
                'id' => $res->id,
            ];
        }
        return [];
    }

    /**
     * 获取消息key
     *
     * @param string $prompt
     * @return string
     */
    public function getDiscordMessageKey(string $prompt = '')
    {
        $prompt = md5($prompt);
        return "discord_message:{$prompt}";
    }
}
