<?php

namespace App\Blog\Controller;

use App\Auth\Entity\User;
use App\Blog\Entity\Blog;
use App\Blog\Repository\BlogRepository;
use App\Shared\Constant\ErrorCodes;
use App\Shared\Controller\BaseController;
use App\Shared\Service\FileValidatorService;
use OpenApi\Attributes as OA;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Attribute\Route;
use Symfony\Component\Security\Http\Attribute\CurrentUser;
use Symfony\Component\Security\Http\Attribute\IsGranted;

#[OA\Tag(name: 'Blog')]
class BlogController extends BaseController
{
    public function __construct(
        private readonly BlogRepository       $blogRepo,
        private readonly FileValidatorService $fileValidator,
        private readonly string               $projectDir,
    ) {}

    // ── Public list/detail ────────────────────────────────────────────────────

    #[OA\Get(
        path: '/api/v1/blogs',
        summary: 'List published blog posts (paginated)',
        parameters: [
            new OA\Parameter(name: 'page', in: 'query', required: false, schema: new OA\Schema(type: 'integer', default: 1)),
            new OA\Parameter(name: 'limit', in: 'query', required: false, schema: new OA\Schema(type: 'integer', default: 20, maximum: 50)),
        ],
        responses: [
            new OA\Response(
                response: 200,
                description: 'Paginated list of published blog posts',
                content: new OA\JsonContent(
                    properties: [
                        new OA\Property(property: 'success', type: 'boolean', example: true),
                        new OA\Property(property: 'data', type: 'array', items: new OA\Items(type: 'object')),
                        new OA\Property(
                            property: 'meta',
                            properties: [
                                new OA\Property(property: 'totalRecords', type: 'integer'),
                                new OA\Property(property: 'totalPages', type: 'integer'),
                                new OA\Property(property: 'currentPage', type: 'integer'),
                            ],
                            type: 'object'
                        ),
                    ]
                )
            ),
        ]
    )]
    #[Route('/api/v1/blogs', methods: ['GET'])]
    public function list(Request $request): JsonResponse
    {
        $page  = max(1, (int) $request->query->get('page', 1));
        $limit = min(50, max(1, (int) $request->query->get('limit', 20)));
        $tag   = $request->query->get('tag') ?: null;

        $blogs = array_map(fn(Blog $b) => $b->toListArray(), $this->blogRepo->findPublished($page, $limit, $tag));
        $total = $this->blogRepo->countPublished($tag);

        return $this->paginated($blogs, $total, $page, $limit);
    }

    #[OA\Get(
        path: '/api/v1/blog/{slug}',
        summary: 'Get a published blog post by slug or UUID',
        parameters: [
            new OA\Parameter(
                name: 'slug',
                in: 'path',
                required: true,
                description: 'Blog slug or UUID',
                schema: new OA\Schema(type: 'string')
            ),
        ],
        responses: [
            new OA\Response(
                response: 200,
                description: 'Blog post detail',
                content: new OA\JsonContent(
                    properties: [
                        new OA\Property(property: 'success', type: 'boolean', example: true),
                        new OA\Property(
                            property: 'data',
                            properties: [
                                new OA\Property(property: 'data', type: 'object'),
                            ],
                            type: 'object'
                        ),
                    ]
                )
            ),
            new OA\Response(response: 404, description: 'Blog post not found or not published'),
        ]
    )]
    #[Route('/api/v1/blog/{slug}', methods: ['GET'])]
    public function detail(string $slug): JsonResponse
    {
        $blog = $this->blogRepo->findBySlug($slug) ?? $this->blogRepo->findByUuid($slug);
        if ($blog === null || $blog->getStatus() !== Blog::STATUS_PUBLISHED) {
            return $this->error(ErrorCodes::ERR_NOT_FOUND_001, 'مقاله یافت نشد', 404);
        }

        return $this->success(['data' => $blog->toArray()]);
    }

    // ── Admin CRUD ────────────────────────────────────────────────────────────

    #[OA\Post(
        path: '/api/v1/blog',
        summary: 'Create a new blog post (admin only)',
        security: [['bearerAuth' => []]],
        requestBody: new OA\RequestBody(
            required: true,
            content: new OA\JsonContent(
                required: ['title', 'body'],
                properties: [
                    new OA\Property(property: 'title', type: 'string'),
                    new OA\Property(property: 'body', type: 'string'),
                    new OA\Property(property: 'summary', type: 'string', nullable: true),
                    new OA\Property(property: 'tags', type: 'array', items: new OA\Items(type: 'string'), nullable: true),
                    new OA\Property(property: 'status', type: 'string', enum: ['draft', 'published'], nullable: true),
                ]
            )
        ),
        responses: [
            new OA\Response(
                response: 201,
                description: 'Blog post created',
                content: new OA\JsonContent(
                    properties: [
                        new OA\Property(property: 'success', type: 'boolean', example: true),
                        new OA\Property(
                            property: 'data',
                            properties: [
                                new OA\Property(property: 'data', type: 'object'),
                            ],
                            type: 'object'
                        ),
                    ]
                )
            ),
            new OA\Response(response: 401, description: 'Unauthorized'),
            new OA\Response(response: 403, description: 'Forbidden — admin role required'),
            new OA\Response(
                response: 422,
                description: 'Validation error',
                content: new OA\JsonContent(
                    properties: [
                        new OA\Property(property: 'success', type: 'boolean', example: false),
                        new OA\Property(
                            property: 'errors',
                            type: 'array',
                            items: new OA\Items(
                                properties: [
                                    new OA\Property(property: 'code', type: 'string'),
                                    new OA\Property(property: 'message', type: 'string'),
                                ]
                            )
                        ),
                    ]
                )
            ),
        ]
    )]
    #[IsGranted('ROLE_ADMIN')]
    #[Route('/api/v1/blog', methods: ['POST'])]
    public function create(Request $request, #[CurrentUser] User $user): JsonResponse
    {
        $data  = json_decode($request->getContent(), true) ?? [];
        if ((isset($data['title']) && !is_string($data['title'])) || (isset($data['body']) && !is_string($data['body']))) {
            return $this->error(ErrorCodes::ERR_VALIDATION_002, 'title و body باید رشته باشند', 422);
        }
        $title = trim((string) ($data['title'] ?? ''));
        $body  = trim((string) ($data['body'] ?? ''));

        if (empty($title) || empty($body)) {
            return $this->error(ErrorCodes::ERR_VALIDATION_002, 'title و body الزامی است', 422);
        }

        $blog = new Blog($user, $title, $body);
        if (!empty($data['summary']))   $blog->setSummary($data['summary']);
        if (!empty($data['tags']))      $blog->setTags((array)$data['tags']);
        if (!empty($data['status']))    $blog->setStatus($data['status']);
        if (!empty($data['image_url'])) $blog->setImageUrl($data['image_url']);

        // Ensure slug uniqueness
        if ($this->blogRepo->findBySlug($blog->getSlug()) !== null) {
            $blog->setSlug($blog->getSlug() . '-' . substr(uniqid(), -4));
        }

        $this->blogRepo->save($blog);

        return $this->success(['data' => $blog->toArray()], 201);
    }

    #[OA\Patch(
        path: '/api/v1/blog/{uuid}',
        summary: 'Update a blog post (admin only)',
        security: [['bearerAuth' => []]],
        requestBody: new OA\RequestBody(
            required: true,
            content: new OA\JsonContent(
                properties: [
                    new OA\Property(property: 'title', type: 'string', nullable: true),
                    new OA\Property(property: 'body', type: 'string', nullable: true),
                    new OA\Property(property: 'summary', type: 'string', nullable: true),
                    new OA\Property(property: 'tags', type: 'array', items: new OA\Items(type: 'string'), nullable: true),
                    new OA\Property(property: 'status', type: 'string', enum: ['draft', 'published'], nullable: true),
                ]
            )
        ),
        parameters: [
            new OA\Parameter(
                name: 'uuid',
                in: 'path',
                required: true,
                schema: new OA\Schema(type: 'string', format: 'uuid')
            ),
        ],
        responses: [
            new OA\Response(
                response: 200,
                description: 'Blog post updated',
                content: new OA\JsonContent(
                    properties: [
                        new OA\Property(property: 'success', type: 'boolean', example: true),
                        new OA\Property(
                            property: 'data',
                            properties: [
                                new OA\Property(property: 'data', type: 'object'),
                            ],
                            type: 'object'
                        ),
                    ]
                )
            ),
            new OA\Response(response: 401, description: 'Unauthorized'),
            new OA\Response(response: 403, description: 'Forbidden — admin role required'),
            new OA\Response(response: 404, description: 'Blog post not found'),
        ]
    )]
    #[IsGranted('ROLE_ADMIN')]
    #[Route('/api/v1/blog/{uuid}', methods: ['PATCH'])]
    public function update(string $uuid, Request $request): JsonResponse
    {
        $blog = $this->blogRepo->findByUuid($uuid);
        if ($blog === null) {
            return $this->error(ErrorCodes::ERR_NOT_FOUND_001, 'مقاله یافت نشد', 404);
        }

        $data = json_decode($request->getContent(), true) ?? [];
        if (array_key_exists('title', $data))   $blog->setTitle($data['title']);
        if (array_key_exists('body', $data))    $blog->setBody($data['body']);
        if (array_key_exists('summary', $data)) $blog->setSummary($data['summary']);
        if (array_key_exists('tags', $data))      $blog->setTags((array)$data['tags']);
        if (array_key_exists('status', $data))    $blog->setStatus($data['status']);
        if (array_key_exists('image_url', $data)) $blog->setImageUrl($data['image_url'] ?: null);

        $this->blogRepo->save($blog);

        return $this->success(['data' => $blog->toArray()]);
    }

    #[OA\Delete(
        path: '/api/v1/blog/{uuid}',
        summary: 'Delete a blog post (admin only)',
        security: [['bearerAuth' => []]],
        parameters: [
            new OA\Parameter(
                name: 'uuid',
                in: 'path',
                required: true,
                schema: new OA\Schema(type: 'string', format: 'uuid')
            ),
        ],
        responses: [
            new OA\Response(
                response: 200,
                description: 'Blog post deleted',
                content: new OA\JsonContent(
                    properties: [
                        new OA\Property(property: 'success', type: 'boolean', example: true),
                        new OA\Property(
                            property: 'data',
                            properties: [
                                new OA\Property(property: 'message', type: 'string'),
                            ],
                            type: 'object'
                        ),
                    ]
                )
            ),
            new OA\Response(response: 401, description: 'Unauthorized'),
            new OA\Response(response: 403, description: 'Forbidden — admin role required'),
            new OA\Response(response: 404, description: 'Blog post not found'),
        ]
    )]
    #[IsGranted('ROLE_ADMIN')]
    #[Route('/api/v1/blog/{uuid}', methods: ['DELETE'])]
    public function delete(string $uuid): JsonResponse
    {
        $blog = $this->blogRepo->findByUuid($uuid);
        if ($blog === null) {
            return $this->error(ErrorCodes::ERR_NOT_FOUND_001, 'مقاله یافت نشد', 404);
        }

        $this->blogRepo->remove($blog);
        return $this->success(['message' => 'مقاله با موفقیت حذف شد']);
    }

    // ── Image upload ──────────────────────────────────────────────────────────

    #[OA\Post(
        path: '/file/upload/clinic_pro/blog/field_image',
        summary: 'Upload a blog post image (admin only)',
        security: [['bearerAuth' => []]],
        requestBody: new OA\RequestBody(
            required: true,
            content: new OA\MediaType(
                mediaType: 'multipart/form-data',
                schema: new OA\Schema(
                    required: ['file'],
                    properties: [
                        new OA\Property(property: 'file', type: 'string', format: 'binary'),
                        new OA\Property(property: 'blog_uuid', type: 'string', format: 'uuid', nullable: true),
                    ]
                )
            )
        ),
        responses: [
            new OA\Response(
                response: 200,
                description: 'Image uploaded',
                content: new OA\JsonContent(
                    properties: [
                        new OA\Property(property: 'success', type: 'boolean', example: true),
                        new OA\Property(
                            property: 'data',
                            properties: [
                                new OA\Property(property: 'image_url', type: 'string'),
                                new OA\Property(property: 'filename', type: 'string'),
                            ],
                            type: 'object'
                        ),
                    ]
                )
            ),
            new OA\Response(response: 401, description: 'Unauthorized'),
            new OA\Response(response: 403, description: 'Forbidden — admin role required'),
            new OA\Response(
                response: 422,
                description: 'No file provided or invalid file',
                content: new OA\JsonContent(
                    properties: [
                        new OA\Property(property: 'success', type: 'boolean', example: false),
                        new OA\Property(
                            property: 'errors',
                            type: 'array',
                            items: new OA\Items(
                                properties: [
                                    new OA\Property(property: 'code', type: 'string'),
                                    new OA\Property(property: 'message', type: 'string'),
                                ]
                            )
                        ),
                    ]
                )
            ),
        ]
    )]
    #[IsGranted('ROLE_ADMIN')]
    #[Route('/file/upload/clinic_pro/blog/field_image', methods: ['POST'])]
    public function uploadImage(Request $request): JsonResponse
    {
        $file     = $request->files->get('file');
        $blogUuid = $request->request->get('blog_uuid', '');

        if ($file === null) {
            return $this->error(ErrorCodes::ERR_FILE_001, 'فایل ارسال نشده است', 422);
        }

        try {
            $safeFilename = $this->fileValidator->validateUploadedFile($file);
        } catch (\App\Shared\Exception\AppException $e) {
            return $this->error($e->getErrorCode(), $e->getMessage(), $e->getHttpStatus());
        }

        $uploadDir = $this->projectDir . '/public/uploads/blogs/';
        if (!is_dir($uploadDir)) mkdir($uploadDir, 0755, true);

        $filename = uniqid('blog_') . '_' . $safeFilename;
        $file->move($uploadDir, $filename);

        $imageUrl  = '/uploads/blogs/' . $filename;
        $imagePath = $uploadDir . $filename;

        if (!empty($blogUuid)) {
            $blog = $this->blogRepo->findByUuid($blogUuid);
            if ($blog !== null) {
                $blog->setImageUrl($imageUrl)->setImagePath($imagePath);
                $this->blogRepo->save($blog);
            }
        }

        return $this->success(['image_url' => $imageUrl, 'filename' => $filename]);
    }
}
