<?php

namespace App\Services;

use Illuminate\Support\Facades\Cache;
use App\Http\Traits\CommonTrait;
use App\Http\Traits\BoardTrait;
use App\Http\Requests\BoardRequest;
use App\Models\Board;
use App\Models\BoardMeta;
use App\Models\BoardCategory;
use App\Models\BoardAdmin;
use App\Models\DTO\SearchData;
use stdClass;

class BoardService
{
    use CommonTrait;
    use BoardTrait;

    public Board $boardModel;
	public BoardMeta $boardMetaModel;
    public BoardCategory $boardCategoryModel;
    public BoardAdmin $boardAdminModel;
    
	public function __construct()
    {
		$this->boardModel = new Board();
		$this->boardMetaModel = new BoardMeta();
		$this->boardCategoryModel = new BoardCategory();
		$this->boardAdminModel = new BoardAdmin();
	}

    /**
     * 게시판 조회
     */
    public function find(string $code): Board
    {
        return $this->boardModel->findByCode($code);
    }

    /**
     * 게시판 설정 값 조회
     */
    public function meta(int $boardID): ?BoardMeta
    {
        return $this->boardMetaModel->getAllMeta($boardID);
    }

    /**
     * 게시판 분류 조회
     */
    public function categories(int $boardID): object|null
    {
        $boardMeta = $this->meta($boardID);
        $useCategory = $boardMeta->item('use_category', 0);
        $usePersonal = $boardMeta->item('use_personal', 0);
        if ($useCategory) {
            if (!$usePersonal || IS_ADMIN) {
                return $this->boardCategoryModel->categories($boardID);
            }
        }
        return null;
    }

	/**
	 * 공지사항 조회
	 */
	public function notices(BoardRequest $request): object|null
	{
        $notices = null;
        if($request->page == 1 && !$request->keyword && !$request->category)
        {
            $notices = $this->boardModel->getNotices(
                SearchData::fromRequest($request)
            );

            if ($notices->total > 0)
            {
                foreach ($notices->list as $i => $row)
                {
                    $notices->list[$i] = $this->_filter($row);
                }
                $notices->list = array_filter($notices->list);
            }
        }

		return $notices;
	}

    /**
     * 일반 게시글 혹은 검색 결과
     */
    public function posts(BoardRequest $request): object
    {
        $posts = $this->boardModel->getPosts(
            SearchData::fromRequest($request)
        );

        if ($posts->total > 0)
        {
            $num = listNum($posts->total, $request->page, $request->perPage);
            foreach ($posts->list as $i => $row)
            {
                $row->num = $num--;
                $posts->list[$i] = $this->_filter($row);
            }
            $posts->list->filter();
        }

       	return $posts;
    }

    /**
     * 회원 작성 게시글 조회
     */
    public function userPosts(int $userID, SearchData $params): object
    {
        $posts = $this->boardModel->getUserPosts($userID, $params);

        if ($posts->total > 0)
        {
            $num = listNum($posts->total, $params->page, $params->perPage);
            foreach ($posts->list as $i => $row)
            {
                $row->num = $num--;
                $posts->list[$i] = $this->_filter($row);
            }
            $posts->list->filter();
        }

        return $posts;
    }

    /**
     * 최신 게시글 검색 조회
     */
    public function search(SearchData $params): object
    {
        $search = $this->boardModel->getSearch($params);

        if ($search->total > 0)
        {
            $num = listNum($search->total, $params->page, $params->perPage);
            foreach ($search->list as $i => $row)
            {
                $row->num = $num--;
                $search->list[$i] = $this->_filter($row);
            }
            $search->list->filter();
        }

        return $search;
    }

    /**
     * 게시판 최근 게시글
     */
    public function latest(?string $code, int $page = 1, int $perPage = DEFAULT_LIST_PER_PAGE)
    {
        $cacheName = sprintf('latest-board-%s-%d-%d', $code, $page, $perPage);
        if (!$latest = Cache::get($cacheName)) {
            $latest = $this->boardModel->getLatest($code, $page, $perPage);
            if($latest->total) {
                $num = listNum($latest->total, $page, $perPage);
                foreach ($latest->list as $i => $row) {
                    $row->num = $num--;
                    $latest->list[$i] = $this->_filter($row);
                }
                $latest->list->filter();
            }
            Cache::tags('latest-board')->put($cacheName, $latest, CACHE_EXPIRE_TIME);
        }
        return $latest;
    }

    /**
     * 게시판 설정 값 조회
     */
    private function _config(string|null $code)
    {
        $boardMeta = $this->meta($code);

        return (object)[
            'use_category' => intval($boardMeta->item('use_category', 0)),
            'subject_length' => (function($boardMeta) {
                // 게시판 설정 값 조회
                if(DEVICE_TYPE_1) {
                    return intval($boardMeta->item('subject_length', 0));
                }else{
                    return intval($boardMeta->item('subject_mobile_length', 0));
                }
            })($boardMeta),
            'new_icon_hour' => intval($boardMeta->item('new_icon_hour', 24)),
            'hot_icon_day' => intval($boardMeta->item('hot_icon_day', 7)),
            'hot_icon_hit' => intval($boardMeta->item('hot_icon_hit', 100)),
            'except_notice' => intval($boardMeta->item('except_notice', 0)),
            'except_speaker' => intval($boardMeta->item('except_speaker', 0)),
            'use_anonymous' => intval($boardMeta->item('use_anonymous', 0)),
            'use_personal' => intval($boardMeta->item('use_personal', 0)),
            'blame_blind_count' => intval($boardMeta->item('blame_blind_count', 0))
        ];
    }

    /**
     * 게시글 가공
     */
    private function _filter($postInfo)
    {
        if ($postInfo)
        {
            $config = $this->_config($postInfo->board_id);

            // 1:1 게시판에서는 전체공지 제외
            if($config->use_personal && $postInfo->is_speaker) {
                return null;
            }

            // 공지 제외
            if ($config->except_notice && $postInfo->is_notice) {
                return null;
            }

            // 전체 공지 제외
            if ($config->except_speaker && $postInfo->is_speaker) {
                return null;
            }

            $postInfo->subject = $this->strCut($postInfo->subject, $config->subject_length);
            $postInfo->viewURL = route('board.post.view', [$postInfo->code, $postInfo->id]) . $this->queryString();
            $postInfo->listURL = route('board.list', [$postInfo->code]);
            $postInfo->createdAt = $this->shortDate($postInfo->created_at);
            $datetime = (time() - strtotime($postInfo->created_at));
            $postInfo->isNew = ($datetime <= ($config->new_icon_hour * 3600)); // 시간 내에 포함하면 표시

            // 시간 내에 포함하거나 조회 수 이상이면 표시
            $postInfo->isHot = ($config->hot_icon_day > 0 ? ($datetime <= ($config->hot_icon_day * 86400)) : true)
                                && ($config->hot_icon_hit > 0 ? ($config->hot_icon_hit <= $postInfo->hit) : false);

            $postInfo->isBlame = ($config->blame_blind_count && $postInfo->blame >= $config->blame_blind_count);
            $postInfo->hit = number_format($postInfo->hit);
            $postInfo->like = number_format($postInfo->like);

            $postInfo->user = new stdClass();
            $postInfo->user->name = ($config->use_anonymous ? '익명' : $postInfo->username);
        }

        return $postInfo;
    }

    /**
     * 게시판 캐시 삭제
     */
    public function clearLatest(string $code): void
    {
        Cache::tags('latest-board-' . $code)->flush();
    }
}
