<?php

namespace App\Http\Controllers\Movie;

use Illuminate\Http\Request;
use Illuminate\Support\Str;
use App\Http\Controllers\Controller;
use App\Http\Traits\CommonTrait;
use App\Models\Movie\Movie;
use App\Models\Movie\MovieInfo;
use App\Models\Movie\MovieDetail;
use App\Models\Movie\MovieReview;
use App\Models\Movie\MovieLike;
use App\Models\Movie\MovieBlame;
use App\Models\DTO\ResponseData;
use App\Models\DTO\SearchData;
use App\Rules\FilterSpamKeyword;
use Exception;

class ReviewController extends Controller
{
    use CommonTrait;

    private Movie $movieModel;
    private MovieInfo $movieInfoModel;
    private MovieDetail $movieDetailModel;
    private MovieReview $movieReviewModel;
    private MovieLike $movieLikeModel;

    public function __construct(
        Movie $movieModel,
        MovieInfo $movieInfoModel,
        MovieDetail $movieDetailModel,
        MovieReview $movieReviewModel,
        MovieLike $movieLikeModel
    ) {
        $this->middleware('front');

        $this->movieModel = $movieModel;
        $this->movieInfoModel = $movieInfoModel;
        $this->movieDetailModel = $movieDetailModel;
        $this->movieReviewModel = $movieReviewModel;
        $this->movieLikeModel = $movieLikeModel;
    }

    /**
     * 영화 평점·후기 목록
     * @method GET
     * @see /movie/review
     */
    public function index(Request $request)
    {
        $params = SearchData::fromRequest($request);
        $params->userID = UID;
        $params->sort = $request->get('sort', 1);

        $reviews = $this->movieReviewModel->list($params);
        if($reviews->rows > 0) {
            foreach($reviews->list as &$row) {
                $length = strlen($row->sid);
                $row->owner = ($row->name . '(' . Str::mask($row->sid, '*', intval($length / 2), $length) . ')');
                $row->createdAt = date('Y.m.d', strtotime($row->created_at));
                $row->isLike = $this->movieLikeModel->isLike($row->id, UID);
                $row->isDisLike = $this->movieLikeModel->isDisLike($row->id, UID);
                $row->viewURL = route('movie.search.show', base64_encode($row->movie_cd));
            }
        }

        return view('movie.review.index', [
            'params' => $params,
            'reviews' => $reviews
        ]);
    }

    /**
     * 영화 평점·후기 보기
     * @method GET
     * @see /movie/review/{movieCd}
     */
    public function show(Request $request)
    {
        $movieCd = base64_decode($request->route('review'));

        $params = SearchData::fromRequest($request);
        $params->movieCd = $movieCd;
        $params->userID = UID;
        $params->sort = $request->get('sort', 1);

        // 이미 등록 했는지 확인
        if(!$this->movieModel->isExists($movieCd)) {
             return alert('잘못된 접근입니다.', DIRECTORY_SEPARATOR);
        }

        $info = $this->movieInfoModel->info($movieCd);
        $detail = $this->movieDetailModel->info($movieCd);
        $avgRate = $this->movieReviewModel->getAvgRate($movieCd);
        $reviews = $this->movieReviewModel->list($params);
        if($reviews->rows > 0) {
            foreach($reviews->list as &$row) {
                $length = strlen($row->sid);
                $row->owner = ($row->name . '(' . Str::mask($row->sid, '*', intval($length / 2), $length) . ')');
                $row->createdAt = date('Y.m.d', strtotime($row->created_at));
                $row->isLike = $this->movieLikeModel->isLike($row->id, UID);
                $row->isDisLike = $this->movieLikeModel->isDisLike($row->id, UID);
            }
        }

        $movieCd = base64_encode($movieCd);
        $movieNm = $info->movie_nm;
        if($info->movie_nm_en) {
            $movieNm .= (' (' . $info->movie_nm_en . ')');
        }

        return view('movie.review.show', [
            'movieCd' => $movieCd,
            'movieNm' => $movieNm,
            'params' => $params,
            'info' => $info,
            'detail' => $detail,
            'reviews' => $reviews,
            'avgRate' => $avgRate
        ]);
    }

    /**
     * 영화 평점·후기 등록
     * @method POST
     * @see /movie/review
     */
    public function store(Request $request, ResponseData $response): ResponseData
    {
        try {

            $request->merge([
                'mid' => base64_decode($request->post('mid'))
            ]);

            $rules = [
                'mid' => 'required|exists:tb_movie,movie_cd',
                'mode' => 'required|in:write',
                'rate' => 'required|numeric|min:1|max:10',
                'content' => ['required', 'string', 'min:1', 'max:1000', new FilterSpamKeyword]
            ];

            $attributes = [
                'mid' => '영화 PK',
                'mode' => '처리 구분',
                'rate' => '평점',
                'content' => '감상평'
            ];

            $messages = [
                'mid.required' => '영화 PK는 필수 값 입니다.',
                'mid.exists' => '영화 PK가 존재하지 않습니다.',
                'mode.required' => '처리 구분을 입력해주세요.',
                'mode.in' => '처리 구분 형식이 옳지 않습니다.',
                'rate.required' => '평점을 선택해주세요.',
                'rate.numeric' => '평점은 형식이 옳지 않습니다.',
                'rate.min' => '평점은 최소 1 이상 입니다.',
                'rate.max' => '평점은 최대 10 이하 입니다.',
                'content.required' => '감상평을 입력해주세요.',
                'content.string' => '감상평 형식이 옳지 않습니다.',
                'content.min' => '감상평을 입력해주세요.',
                'content.max' => '감상평은 최대 1000자 입력 가능합니다.'
            ];

            $posts = $this->validate($request, $rules, $messages, $attributes);

            // 이미 등록 했는지 확인
            if($this->movieReviewModel->isAlready($posts['mid'], UID)) {
                throw new Exception('이미 감상 후기를 등록하셨습니다.');
            }

            $this->movieReviewModel->insert([
                'movie_cd' => $posts['mid'],
                'user_id' => UID,
                'content' => trim($posts['content']),
                'rate' => $posts['rate'],
                'like' => 0,
                'dislike' => 0,
                'blame' => 0,
                'ip_address' => IP_ADDRESS,
                'user_agent' => USER_AGENT,
                'updated_at' => null,
                'created_at' => now()
            ]);

        }catch(Exception $e) {
            $response = $response::fromException($e);
        }

        return $response;
    }

    /**
     * 영화 평점·후기 수정
     * @method PUT
     * @see /movie/review/{review_id}
     */
    public function update(Request $request, ResponseData $response): ResponseData
    {
        try {

            $request->merge([
                'mid' => base64_decode($request->post('mid'))
            ]);

            $rules = [
                'mid' => 'required|exists:tb_movie,movie_cd',
                'rid' => 'required|exists:tb_movie_review,id',
                'mode' => 'required|in:modify',
                'rate' => 'required|numeric|min:1|max:10',
                'content' => ['required', 'string', 'min:1', 'max:1000', new FilterSpamKeyword]
            ];

            $attributes = [
                'rid' => '후기 PK',
                'mid' => '영화 PK',
                'mode' => '처리 구분',
                'rate' => '평점',
                'content' => '감상평'
            ];

            $messages = [
                'mid.required' => '영화 PK는 필수 값 입니다.',
                'mid.exists' => '영화 PK가 존재하지 않습니다.',
                'rid.required' => '후기 PK는 필수 값 입니다.',
                'rid.exists' => '후기 PK가 존재하지 않습니다.',
                'mode.required' => '처리 구분을 입력해주세요.',
                'mode.in' => '처리 구분 형식이 옳지 않습니다.',
                'rate.required' => '평점을 선택해주세요.',
                'rate.numeric' => '평점은 형식이 옳지 않습니다.',
                'rate.min' => '평점은 최소 1 이상 입니다.',
                'rate.max' => '평점은 최대 10 이하 입니다.',
                'content.required' => '감상평을 입력해주세요.',
                'content.string' => '감상평 형식이 옳지 않습니다.',
                'content.min' => '감상평을 입력해주세요.',
                'content.max' => '감상평은 최대 1000자 입력 가능합니다.'
            ];

            $posts = $this->validate($request, $rules, $messages, $attributes);

            // 내가 등록한 후기 인지 확인
            if(!$this->movieReviewModel->isExists($posts['rid'], UID)) {
                throw new Exception('내가 등록한 감상 후기가 아닙니다.');
            }

            $this->movieReviewModel->find($posts['rid'])->update([
                'content' => trim($posts['content']),
                'rate' => $posts['rate'],
                'updated_at' => now()
            ]);

        }catch(Exception $e) {
            $response = $response::fromException($e);
        }

        return $response;
    }

    /**
     * 영화 평점·후기 삭제
     * @method DELETE
     * @see /movie/review/{review_id}
     */
    public function destroy(Request $request, ResponseData $response): ResponseData
    {
        try {

            $request->merge([
                'mid' => base64_decode($request->post('mid'))
            ]);

            $rules = [
                'mid' => 'required|exists:tb_movie,movie_cd',
                'rid' => 'required|exists:tb_movie_review,id',
                'mode' => 'required|in:delete'
            ];

            $attributes = [
                'rid' => '후기 PK',
                'mid' => '영화 PK',
                'mode' => '처리 구분'
            ];

            $messages = [
                'mid.required' => '영화 PK는 필수 값 입니다.',
                'mid.exists' => '영화 PK가 존재하지 않습니다.',
                'rid.required' => '후기 PK는 필수 값 입니다.',
                'rid.exists' => '후기 PK가 존재하지 않습니다.',
                'mode.required' => '처리 구분을 입력해주세요.',
                'mode.in' => '처리 구분 형식이 옳지 않습니다.'
            ];

            $posts = $this->validate($request, $rules, $messages, $attributes);

            // 내가 등록한 후기 인지 확인
            if(!$this->movieReviewModel->isExists($posts['rid'], UID)) {
                throw new Exception('내가 등록한 감상 후기가 아닙니다.');
            }

            $this->movieReviewModel->find($posts['rid'])->delete();

        }catch(Exception $e) {
            $response = $response::fromException($e);
        }

        return $response;
    }

    /**
     * 평점·후기 공감하기
     * @method POST
     * @see /movie/review/like
     */
    public function like(Request $request, ResponseData $response): ResponseData
    {
        try {

            $request->merge([
                'mid' => base64_decode($request->post('mid'))
            ]);

            $rules = [
                'mid' => 'required|exists:tb_movie,movie_cd',
                'rid' => 'required|exists:tb_movie_review,id'
            ];

            $attributes = [
                'mid' => '영화 PK',
                'rid' => '후기 PK'
            ];

            $messages = [
                'mid.required' => '영화 PK는 필수 값 입니다.',
                'mid.exists' => '영화 PK가 존재하지 않습니다.',
                'rid.required' => '후기 PK는 필수 값 입니다.',
                'rid.exists' => '후기 PK가 존재하지 않습니다.',
            ];

            $posts = $this->validate($request, $rules, $messages, $attributes);

            $like = $this->movieLikeModel->getType($posts['rid'], UID);
            if($like->exists) {
                throw new Exception(sprintf("이미 %s 하셨습니다.", ($like->type == 1 ? '공감' : '비공감')));
            }

            $response->like = $this->movieReviewModel->setLike($posts['mid'], $posts['rid'], UID, LIKE);

        }catch(Exception $e) {
            $response = $response::fromException($e);
        }

        return $response;
    }

    /**
     * 평점·후기 비공감하기
     * @method POST
     * @see /movie/review/dislike
     */
    public function dislike(Request $request, ResponseData $response): ResponseData
    {
        try {

            $request->merge([
                'mid' => base64_decode($request->post('mid'))
            ]);

            $rules = [
                'mid' => 'required|exists:tb_movie,movie_cd',
                'rid' => 'required|exists:tb_movie_review,id'
            ];

            $attributes = [
                'mid' => '영화 PK',
                'rid' => '후기 PK',
            ];

            $messages = [
                'mid.required' => '영화 PK는 필수 값 입니다.',
                'mid.exists' => '영화 PK가 존재하지 않습니다.',
                'rid.required' => '후기 PK는 필수 값 입니다.',
                'rid.exists' => '후기 PK가 존재하지 않습니다.'
            ];

            $posts = $this->validate($request, $rules, $messages, $attributes);

            $like = $this->movieLikeModel->getType($posts['rid'], UID);
            if($like->exists) {
                throw new Exception(sprintf("이미 %s 하셨습니다.", ($like->type == 1 ? '공감' : '비공감')));
            }

            $response->dislike = $this->movieReviewModel->setLike($posts['mid'], $posts['rid'], UID, DISLIKE);

        }catch(Exception $e) {
            $response = $response::fromException($e);
        }

        return $response;
    }

    /**
     * 평점·후기 신고
     * @method POST
     * @see /movie/review/blame
     */
    public function blame(Request $request, ResponseData $response, MovieBlame $movieBlameModel): ResponseData
    {
        try {

            $request->merge([
                'mid' => base64_decode($request->post('mid'))
            ]);

            $rules = [
                'mid' => 'required|exists:tb_movie,movie_cd',
                'rid' => 'required|exists:tb_movie_review,id',
                'blame_type' => 'required|numeric|in:1,2,3,4,5,6,7,8,9',
                'blame_reason' => 'required|string|max:1000'
            ];

            $attributes = [
                'rid' => '후기 PK',
                'mid' => '영화 PK',
                'blame_type' => '신고 사유',
                'blame_reason' => '신고 내용'
            ];

            $messages = [
                'mid.required' => '영화 PK는 필수 값 입니다.',
                'mid.exists' => '영화 PK가 존재하지 않습니다.',
                'rid.required' => '후기 PK는 필수 값 입니다.',
                'rid.exists' => '후기 PK가 존재하지 않습니다.',
                'blame_type.required' => '신고 사유를 선택해주세요.',
                'blame_type.numeric' => '신고 사유 형식이 옳지 않습니다.',
                'blame_type.in' => '신고 사유가 잘못되었습니다.',
                'blame_reason.required' => '신고 내용을 입력해주세요.',
                'blame_reason.string' => '신고 내용 형식이 옳지 않습니다.',
                'blame_reason.max' => '신고 내용은 최대 1000자 입력 가능합니다.'
            ];

            $posts = $this->validate($request, $rules, $messages, $attributes);

            if($movieBlameModel->isAlready($posts['mid'], $posts['rid'], UID)) {
                throw new Exception('이미 신고가 접수되었습니다.');
            }

            $this->movieReviewModel->setBlame($posts['mid'], $posts['rid'], UID, $posts['blame_type'], $posts['blame_reason']);

        }catch(Exception $e) {
            $response = $response::fromException($e);
        }

        return $response;
    }
}
