<?php

namespace App\Services\Boards;

use App\Models\Boards\Board;
use App\Models\Boards\BoardComment;
use App\Models\Boards\Post;
use App\Models\Boards\PostCustomFieldValue;
use App\Services\Service;
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
use Illuminate\Database\Eloquent\Collection;

class PostService extends Service
{
    /**
     * 본사 테넌트 ID (시스템 게시판에서 본사 글은 모든 테넌트에게 공개)
     */
    private const HQ_TENANT_ID = 1;

    /**
     * 시스템 게시판 조회 조건 적용
     * - 본사(HQ_TENANT_ID) 글은 모든 테넌트에게 공개
     * - 각 테넌트 글은 해당 테넌트만 조회 가능
     */
    private function applySystemBoardScope($query): void
    {
        $query->where(function ($q) {
            $q->where('tenant_id', self::HQ_TENANT_ID)
                ->orWhere('tenant_id', $this->tenantId());
        });
    }

    // =========================================================================
    // 게시글 조회 메서드
    // =========================================================================

    /**
     * 게시글 목록 조회 (페이징)
     *
     * @param  bool  $isSystemBoard  시스템 게시판 여부
     *                               - 시스템 게시판: 본사(tenant_id=1) 글 + 현재 테넌트 글 조회
     *                               - 일반 게시판: 현재 테넌트 글만 조회
     */
    public function getPostsByBoard(int $boardId, array $filters = [], int $perPage = 15, bool $isSystemBoard = false): LengthAwarePaginator
    {
        return Post::where('board_id', $boardId)
            ->when($isSystemBoard, function ($q) {
                // 시스템 게시판: 본사(HQ) 글 + 현재 테넌트 글
                $q->where(function ($query) {
                    $query->where('tenant_id', self::HQ_TENANT_ID)
                        ->orWhere('tenant_id', $this->tenantId());
                });
            }, fn ($q) => $q->where('tenant_id', $this->tenantId()))
            ->when(isset($filters['search']), function ($q) use ($filters) {
                $q->where(function ($query) use ($filters) {
                    $query->where('title', 'like', "%{$filters['search']}%")
                        ->orWhere('content', 'like', "%{$filters['search']}%");
                });
            })
            ->when(isset($filters['is_notice']), fn ($q) => $q->where('is_notice', $filters['is_notice']))
            ->when(isset($filters['status']), fn ($q) => $q->where('status', $filters['status']))
            ->orderByDesc('is_notice')
            ->orderByDesc('created_at')
            ->paginate($perPage);
    }

    /**
     * 게시글 목록 조회 (게시판 코드 기반)
     */
    public function getPostsByBoardCode(string $boardCode, array $filters = [], int $perPage = 15): LengthAwarePaginator
    {
        $board = Board::accessible($this->tenantId())
            ->where('board_code', $boardCode)
            ->firstOrFail();

        return $this->getPostsByBoard($board->id, $filters, $perPage, $board->is_system);
    }

    /**
     * 시스템 게시판 게시글 목록 조회 (게시판 코드 기반)
     */
    public function getPostsBySystemBoardCode(string $boardCode, array $filters = [], int $perPage = 15): LengthAwarePaginator
    {
        $board = Board::systemOnly()
            ->where('board_code', $boardCode)
            ->where('is_active', true)
            ->firstOrFail();

        return $this->getPostsByBoard($board->id, $filters, $perPage, true);
    }

    /**
     * 테넌트 게시판 게시글 목록 조회 (게시판 코드 기반)
     */
    public function getPostsByTenantBoardCode(string $boardCode, array $filters = [], int $perPage = 15): LengthAwarePaginator
    {
        $board = Board::tenantOnly($this->tenantId())
            ->where('board_code', $boardCode)
            ->where('is_active', true)
            ->firstOrFail();

        return $this->getPostsByBoard($board->id, $filters, $perPage, false);
    }

    /**
     * 게시글 단건 조회
     *
     * @param  bool|null  $isSystemBoard  시스템 게시판 여부 (null이면 자동 감지)
     */
    public function getPost(int $postId, ?bool $isSystemBoard = null): ?Post
    {
        $query = Post::with(['files', 'comments.replies', 'board']);

        if ($isSystemBoard === null) {
            // 게시글에서 board 정보로 시스템 게시판 여부 판단
            $query->where(function ($q) {
                // 현재 테넌트 글
                $q->where('tenant_id', $this->tenantId())
                    // 또는 시스템 게시판의 본사 글
                    ->orWhere(function ($sub) {
                        $sub->where('tenant_id', self::HQ_TENANT_ID)
                            ->whereHas('board', fn ($b) => $b->where('is_system', true));
                    });
            });
        } elseif ($isSystemBoard) {
            // 시스템 게시판: 본사 글 + 현재 테넌트 글
            $this->applySystemBoardScope($query);
        } else {
            $query->where('tenant_id', $this->tenantId());
        }

        return $query->find($postId);
    }

    /**
     * 게시글 상세 조회 (조회수 증가)
     */
    public function getPostWithViewIncrement(int $postId): ?Post
    {
        $post = $this->getPost($postId);

        if ($post) {
            $post->increment('views');
        }

        return $post;
    }

    /**
     * 게시판 코드와 게시글 ID로 조회
     */
    public function getPostByCodeAndId(string $boardCode, int $postId): ?Post
    {
        $board = Board::accessible($this->tenantId())
            ->where('board_code', $boardCode)
            ->first();

        if (! $board) {
            return null;
        }

        $query = Post::with(['files', 'comments.replies', 'board'])
            ->where('board_id', $board->id);

        // 시스템 게시판: 본사 글 + 현재 테넌트 글
        // 일반 게시판: 현재 테넌트 게시글만 조회
        if ($board->is_system) {
            $this->applySystemBoardScope($query);
        } else {
            $query->where('tenant_id', $this->tenantId());
        }

        return $query->find($postId);
    }

    /**
     * 시스템 게시판 코드와 게시글 ID로 조회
     */
    public function getPostBySystemBoardCodeAndId(string $boardCode, int $postId): ?Post
    {
        $board = Board::systemOnly()
            ->where('board_code', $boardCode)
            ->where('is_active', true)
            ->first();

        if (! $board) {
            return null;
        }

        $query = Post::with(['files', 'comments.replies', 'board'])
            ->where('board_id', $board->id);

        // 시스템 게시판: 본사 글 + 현재 테넌트 글
        $this->applySystemBoardScope($query);

        return $query->find($postId);
    }

    /**
     * 테넌트 게시판 코드와 게시글 ID로 조회
     */
    public function getPostByTenantBoardCodeAndId(string $boardCode, int $postId): ?Post
    {
        $board = Board::tenantOnly($this->tenantId())
            ->where('board_code', $boardCode)
            ->where('is_active', true)
            ->first();

        if (! $board) {
            return null;
        }

        return Post::with(['files', 'comments.replies', 'board'])
            ->where('board_id', $board->id)
            ->where('tenant_id', $this->tenantId())
            ->find($postId);
    }

    // =========================================================================
    // 게시글 생성 메서드
    // =========================================================================

    /**
     * 게시글 생성
     *
     * @param  bool  $isSystemBoard  시스템 게시판 여부 (시스템 게시판은 tenant_id = null)
     */
    public function createPost(int $boardId, array $data, bool $isSystemBoard = false): Post
    {
        $data['board_id'] = $boardId;
        $data['tenant_id'] = $this->tenantId(); // 시스템 게시판도 tenant_id 설정 (본사=1의 글만 전체 공개)
        $data['user_id'] = $this->apiUserId();
        $data['ip_address'] = request()->ip();
        $data['status'] = $data['status'] ?? 'published';
        $data['views'] = 0;

        $post = Post::create($data);

        // 커스텀 필드 저장
        if (isset($data['custom_fields'])) {
            $this->saveCustomFields($post->id, $boardId, $data['custom_fields']);
        }

        return $post->fresh(['board', 'files']);
    }

    /**
     * 게시글 생성 (게시판 코드 기반)
     */
    public function createPostByBoardCode(string $boardCode, array $data): Post
    {
        $board = Board::accessible($this->tenantId())
            ->where('board_code', $boardCode)
            ->firstOrFail();

        return $this->createPost($board->id, $data, $board->is_system);
    }

    /**
     * 시스템 게시판 게시글 생성 (게시판 코드 기반)
     */
    public function createPostBySystemBoardCode(string $boardCode, array $data): Post
    {
        $board = Board::systemOnly()
            ->where('board_code', $boardCode)
            ->where('is_active', true)
            ->firstOrFail();

        return $this->createPost($board->id, $data, true);
    }

    /**
     * 테넌트 게시판 게시글 생성 (게시판 코드 기반)
     */
    public function createPostByTenantBoardCode(string $boardCode, array $data): Post
    {
        $board = Board::tenantOnly($this->tenantId())
            ->where('board_code', $boardCode)
            ->where('is_active', true)
            ->firstOrFail();

        return $this->createPost($board->id, $data, false);
    }

    // =========================================================================
    // 게시글 수정 메서드
    // =========================================================================

    /**
     * 게시글 수정
     *
     * @param  bool|null  $isSystemBoard  시스템 게시판 여부 (null이면 자동 감지)
     */
    public function updatePost(int $postId, array $data, ?bool $isSystemBoard = null): ?Post
    {
        $query = Post::with('board');

        if ($isSystemBoard === null) {
            // 게시글에서 board 정보로 시스템 게시판 여부 판단
            $query->where(function ($q) {
                // 현재 테넌트 글
                $q->where('tenant_id', $this->tenantId())
                    // 또는 시스템 게시판의 본사 글
                    ->orWhere(function ($sub) {
                        $sub->where('tenant_id', self::HQ_TENANT_ID)
                            ->whereHas('board', fn ($b) => $b->where('is_system', true));
                    });
            });
        } elseif ($isSystemBoard) {
            // 시스템 게시판: 본사 글 + 현재 테넌트 글
            $this->applySystemBoardScope($query);
        } else {
            $query->where('tenant_id', $this->tenantId());
        }

        $post = $query->find($postId);

        if (! $post) {
            return null;
        }

        $post->update($data);

        // 커스텀 필드 업데이트
        if (isset($data['custom_fields'])) {
            $this->saveCustomFields($post->id, $post->board_id, $data['custom_fields']);
        }

        return $post->fresh(['board', 'files']);
    }

    /**
     * 게시글 수정 (게시판 코드 기반)
     */
    public function updatePostByBoardCode(string $boardCode, int $postId, array $data): ?Post
    {
        $board = Board::accessible($this->tenantId())
            ->where('board_code', $boardCode)
            ->first();

        if (! $board) {
            return null;
        }

        $query = Post::where('board_id', $board->id);

        // 시스템 게시판: 본사 글 + 현재 테넌트 글
        // 일반 게시판: 현재 테넌트 게시글만
        if ($board->is_system) {
            $this->applySystemBoardScope($query);
        } else {
            $query->where('tenant_id', $this->tenantId());
        }

        $post = $query->find($postId);

        if (! $post) {
            return null;
        }

        $post->update($data);

        if (isset($data['custom_fields'])) {
            $this->saveCustomFields($post->id, $board->id, $data['custom_fields']);
        }

        return $post->fresh(['board', 'files']);
    }

    /**
     * 시스템 게시판 게시글 수정 (게시판 코드 기반)
     */
    public function updatePostBySystemBoardCode(string $boardCode, int $postId, array $data): ?Post
    {
        $board = Board::systemOnly()
            ->where('board_code', $boardCode)
            ->where('is_active', true)
            ->first();

        if (! $board) {
            return null;
        }

        $query = Post::where('board_id', $board->id);

        // 시스템 게시판: 본사 글 + 현재 테넌트 글
        $this->applySystemBoardScope($query);

        $post = $query->find($postId);

        if (! $post) {
            return null;
        }

        $post->update($data);

        if (isset($data['custom_fields'])) {
            $this->saveCustomFields($post->id, $board->id, $data['custom_fields']);
        }

        return $post->fresh(['board', 'files']);
    }

    /**
     * 테넌트 게시판 게시글 수정 (게시판 코드 기반)
     */
    public function updatePostByTenantBoardCode(string $boardCode, int $postId, array $data): ?Post
    {
        $board = Board::tenantOnly($this->tenantId())
            ->where('board_code', $boardCode)
            ->where('is_active', true)
            ->first();

        if (! $board) {
            return null;
        }

        $post = Post::where('board_id', $board->id)
            ->where('tenant_id', $this->tenantId())
            ->find($postId);

        if (! $post) {
            return null;
        }

        $post->update($data);

        if (isset($data['custom_fields'])) {
            $this->saveCustomFields($post->id, $board->id, $data['custom_fields']);
        }

        return $post->fresh(['board', 'files']);
    }

    // =========================================================================
    // 게시글 삭제 메서드
    // =========================================================================

    /**
     * 게시글 삭제 (Soft Delete)
     *
     * @param  bool|null  $isSystemBoard  시스템 게시판 여부 (null이면 자동 감지)
     */
    public function deletePost(int $postId, ?bool $isSystemBoard = null): bool
    {
        $query = Post::with('board');

        if ($isSystemBoard === null) {
            // 게시글에서 board 정보로 시스템 게시판 여부 판단
            $query->where(function ($q) {
                // 현재 테넌트 글
                $q->where('tenant_id', $this->tenantId())
                    // 또는 시스템 게시판의 본사 글
                    ->orWhere(function ($sub) {
                        $sub->where('tenant_id', self::HQ_TENANT_ID)
                            ->whereHas('board', fn ($b) => $b->where('is_system', true));
                    });
            });
        } elseif ($isSystemBoard) {
            // 시스템 게시판: 본사 글 + 현재 테넌트 글
            $this->applySystemBoardScope($query);
        } else {
            $query->where('tenant_id', $this->tenantId());
        }

        $post = $query->find($postId);

        if (! $post) {
            return false;
        }

        return $post->delete();
    }

    /**
     * 게시글 삭제 (게시판 코드 기반)
     */
    public function deletePostByBoardCode(string $boardCode, int $postId): bool
    {
        $board = Board::accessible($this->tenantId())
            ->where('board_code', $boardCode)
            ->first();

        if (! $board) {
            return false;
        }

        $query = Post::where('board_id', $board->id);

        // 시스템 게시판: 본사 글 + 현재 테넌트 글
        // 일반 게시판: 현재 테넌트 게시글만
        if ($board->is_system) {
            $this->applySystemBoardScope($query);
        } else {
            $query->where('tenant_id', $this->tenantId());
        }

        $post = $query->find($postId);

        if (! $post) {
            return false;
        }

        return $post->delete();
    }

    /**
     * 시스템 게시판 게시글 삭제 (게시판 코드 기반)
     */
    public function deletePostBySystemBoardCode(string $boardCode, int $postId): bool
    {
        $board = Board::systemOnly()
            ->where('board_code', $boardCode)
            ->where('is_active', true)
            ->first();

        if (! $board) {
            return false;
        }

        $query = Post::where('board_id', $board->id);

        // 시스템 게시판: 본사 글 + 현재 테넌트 글
        $this->applySystemBoardScope($query);

        $post = $query->find($postId);

        if (! $post) {
            return false;
        }

        return $post->delete();
    }

    /**
     * 테넌트 게시판 게시글 삭제 (게시판 코드 기반)
     */
    public function deletePostByTenantBoardCode(string $boardCode, int $postId): bool
    {
        $board = Board::tenantOnly($this->tenantId())
            ->where('board_code', $boardCode)
            ->where('is_active', true)
            ->first();

        if (! $board) {
            return false;
        }

        $post = Post::where('board_id', $board->id)
            ->where('tenant_id', $this->tenantId())
            ->find($postId);

        if (! $post) {
            return false;
        }

        return $post->delete();
    }

    // =========================================================================
    // 댓글 메서드
    // =========================================================================

    /**
     * 댓글 목록 조회
     */
    public function getComments(int $postId): Collection
    {
        return BoardComment::where('post_id', $postId)
            ->whereNull('parent_id')
            ->where('status', 'active')
            ->with(['user', 'replies.user'])
            ->orderBy('created_at')
            ->get();
    }

    /**
     * 댓글 작성
     */
    public function createComment(int $postId, array $data): BoardComment
    {
        $data['post_id'] = $postId;
        $data['tenant_id'] = $this->tenantId();
        $data['user_id'] = $this->apiUserId();
        $data['ip_address'] = request()->ip();
        $data['status'] = 'active';

        $comment = BoardComment::create($data);

        return $comment->load('user');
    }

    /**
     * 댓글 수정
     */
    public function updateComment(int $commentId, array $data): ?BoardComment
    {
        $comment = BoardComment::where('user_id', $this->apiUserId())
            ->find($commentId);

        if (! $comment) {
            return null;
        }

        $comment->update($data);

        return $comment->fresh('user');
    }

    /**
     * 댓글 삭제
     */
    public function deleteComment(int $commentId): bool
    {
        $comment = BoardComment::where('user_id', $this->apiUserId())
            ->find($commentId);

        if (! $comment) {
            return false;
        }

        $comment->status = 'deleted';
        $comment->save();

        return true;
    }

    // =========================================================================
    // 커스텀 필드 메서드
    // =========================================================================

    /**
     * 커스텀 필드 값 저장
     *
     * @param  int  $postId  게시글 ID
     * @param  int  $boardId  게시판 ID (field_key → field_id 변환용)
     * @param  array  $customFields  [field_key => value] 또는 [field_id => value]
     */
    protected function saveCustomFields(int $postId, int $boardId, array $customFields): void
    {
        // field_key → field_id 매핑 조회
        $fieldMap = \App\Models\Boards\BoardSetting::where('board_id', $boardId)
            ->pluck('id', 'field_key')
            ->toArray();

        foreach ($customFields as $fieldKey => $value) {
            // field_key가 문자열이면 field_id로 변환
            $fieldId = is_numeric($fieldKey) ? (int) $fieldKey : ($fieldMap[$fieldKey] ?? null);

            if ($fieldId === null) {
                continue; // 존재하지 않는 필드는 무시
            }

            PostCustomFieldValue::updateOrCreate(
                [
                    'post_id' => $postId,
                    'field_id' => $fieldId,
                ],
                [
                    'value' => is_array($value) ? json_encode($value) : $value,
                ]
            );
        }
    }

    /**
     * 커스텀 필드 값 조회
     */
    public function getCustomFieldValues(int $postId): Collection
    {
        return PostCustomFieldValue::where('post_id', $postId)
            ->with('field')
            ->get();
    }

    // =========================================================================
    // 나의 게시글 메서드
    // =========================================================================

    /**
     * 나의 게시글 목록 조회
     * 현재 테넌트에서 내가 작성한 게시글 조회
     */
    public function getMyPosts(array $filters = [], int $perPage = 15): LengthAwarePaginator
    {
        return Post::where('user_id', $this->apiUserId())
            ->where('tenant_id', $this->tenantId())
            ->with(['board:id,board_code,name,is_system'])
            ->when(isset($filters['board_code']), function ($q) use ($filters) {
                $q->whereHas('board', fn ($query) => $query->where('board_code', $filters['board_code']));
            })
            ->when(isset($filters['search']), function ($q) use ($filters) {
                $q->where(function ($query) use ($filters) {
                    $query->where('title', 'like', "%{$filters['search']}%")
                        ->orWhere('content', 'like', "%{$filters['search']}%");
                });
            })
            ->when(isset($filters['status']), fn ($q) => $q->where('status', $filters['status']))
            ->orderByDesc('created_at')
            ->paginate($perPage);
    }
}
