<?php

namespace App\Http\Controllers\Admin;

use App\Http\Controllers\Controller;
use App\Models\Category;
use Illuminate\Http\Request;
use Illuminate\Support\Str;
use Inertia\Inertia;

class CategoryBaseController extends Controller
{
    private int $_type = 0;

    private string $_module = 'mattock';

    private array $_mattockCategorySlugBlock = ['ai-chatbot'];

    public function __construct($module = null)
    {
        if ($module == 'pipopa') {
            $this->_type = 1;
            $this->_module = 'pipopa';
        }
    }

    /**
     * Display a listing of the resource.
     */
    public function index(Request $request)
    {
        $search = $request->input('search');
        $perPage = $request->input('per_page', 10);

        $categories = Category::with(['parent', 'children'])
            ->where('type', $this->_type)
            ->when($search, function ($query, $search) {
                return $query->where('name', 'like', "%{$search}%")
                    ->orWhere('slug', 'like', "%{$search}%")
                    ->orWhere('description', 'like', "%{$search}%");
            })
            ->orderBy('created_at', 'desc')
            ->paginate($perPage);

        // Get root categories for dropdown
        $rootCategories = Category::roots()->where('type', $this->_type)->get();

        return Inertia::render("admin/{$this->_module}/categories/Index", [
            'categories'     => $categories,
            'rootCategories' => $rootCategories,
            'filters'        => [
                'search'   => $search,
                'per_page' => $perPage,
            ],
        ]);
    }

    /**
     * Show the form for creating a new resource.
     */
    public function create()
    {
        $parentCategories = Category::where('type', $this->_type)->get();

        return Inertia::render("admin/{$this->_module}/categories/Create", [
            'parentCategories' => $parentCategories,
        ]);
    }

    /**
     * Store a newly created resource in storage.
     */
    public function store(Request $request)
    {
        $validated = $request->validate([
            'name'        => 'required|string|max:255',
            'slug'        => 'nullable|string|max:255',
            'description' => 'nullable|string',
            'parent_id'   => 'nullable|integer|exists:categories,id',
        ]);

        $checkCategoryName = Category::where('type', $this->_type)->where('name', $validated['name'])->exists();
        if ($checkCategoryName) {
            return back()->withErrors(['name' => 'Category name already exists.']);
        }

        if (isset($validated['slug']) && $validated['slug'] != '') {
            // Sanitize the slug
            $validated['slug'] = $this->sanitizeSlug($validated['slug']);

            $checkCategorySlug = Category::where('type', $this->_type)->where('slug', $validated['slug'])->exists();
            if ($checkCategorySlug || in_array($validated['slug'], $this->_mattockCategorySlugBlock)) {
                return back()->withErrors(['slug' => 'Category URL slug already exists.']);
            }
        } else {
            $slugPrefix = 0;
            do {
                $slug = Str::slug($this->convertJapaneseToRomaji(trim($validated['name'])));
                if ($slugPrefix > 0) {
                    $slug .= '-' . $slugPrefix;
                }
                $checkSlug = Category::where('type', $this->_type)->where('slug', $slug)->exists();
                $slugPrefix++;
            } while ($checkSlug || in_array($slug, $this->_mattockCategorySlugBlock));

            $validated['slug'] = $slug;
        }

        // Set parent_id to 0 if not provided
        if (! isset($validated['parent_id'])) {
            $validated['parent_id'] = 0;
        }

        $validated['type'] = $this->_type;
        Category::create($validated);

        return redirect()
            ->route("admin.{$this->_module}.categories.index")
            ->with('success', 'Category created successfully.');
    }

    /**
     * Display the specified resource.
     */
    public function show(Category $category)
    {
        if ($category->type != $this->_type) {
            return redirect()
                ->route('admin.pages.404')
                ->with('error', 'Category not found.');
        }

        $category->load(['parent', 'children', 'articles']);

        return Inertia::render("admin/{$this->_module}/categories/Show", [
            'category' => $category,
        ]);
    }

    /**
     * Show the form for editing the specified resource.
     */
    public function edit(Category $category)
    {
        if ($category->type != $this->_type) {
            return redirect()
                ->route('admin.pages.404')
                ->with('error', 'Category not found.');
        }

        $parentCategories = Category::where('id', '!=', $category->id)
            ->where('type', $this->_type)
            ->where(function ($query) {
                $query->roots()->orWhere('parent_id', '!=', 0);
            })
            ->get();

        return Inertia::render("admin/{$this->_module}/categories/Edit", [
            'category'         => $category,
            'parentCategories' => $parentCategories,
        ]);
    }

    /**
     * Update the specified resource in storage.
     */
    public function update(Request $request, Category $category)
    {
        $validated = $request->validate([
            'name'        => 'required|string|max:255',
            'slug'        => 'nullable|string|max:255',
            'description' => 'nullable|string',
            'parent_id'   => 'nullable|integer|exists:categories,id|not_in:' . $category->id,
        ]);

        if ($category->type != $this->_type) {
            return redirect()
                ->route('admin.pages.404')
                ->with('error', 'Category not found.');
        }

        $checkCategoryName = Category::where('type', $this->_type)
            ->where('id', '!=', $category->id)
            ->where('name', $validated['name'])
            ->exists();
        if ($checkCategoryName) {
            return back()->withErrors(['name' => 'Category name already exists.']);
        }

        if (isset($validated['slug']) && $validated['slug'] != '') {
            // Sanitize the slug
            $validated['slug'] = $this->sanitizeSlug($validated['slug']);

            $checkCategorySlug = Category::where('type', $this->_type)
                ->where('id', '!=', $category->id)
                ->where('slug', $validated['slug'])
                ->exists();
            if ($checkCategorySlug || in_array($validated['slug'], $this->_mattockCategorySlugBlock)) {
                return back()->withErrors(['slug' => 'Category URL slug already exists.']);
            }
        } else {
            $slugPrefix = 0;
            do {
                $slug = Str::slug($this->convertJapaneseToRomaji(trim($validated['name'])));
                if ($slugPrefix > 0) {
                    $slug .= '-' . $slugPrefix;
                }
                $checkSlug = Category::where('type', $this->_type)->where('slug', $slug)->exists();
                $slugPrefix++;
            } while ($checkSlug || in_array($slug, $this->_mattockCategorySlugBlock));

            $validated['slug'] = $slug;
        }

        // Set parent_id to 0 if not provided
        if (! isset($validated['parent_id'])) {
            $validated['parent_id'] = 0;
        }

        // Prevent circular reference
        if ($validated['parent_id'] !== 0) {
            $parent = Category::where('type', $this->_type)->find($validated['parent_id']);
            if ($parent && $this->isDescendant($category, $parent)) {
                return back()->withErrors(['parent_id' => 'Cannot set a descendant as parent.']);
            }
        }

        // Update category
        $category->update($validated);

        return redirect()
            ->route("admin.{$this->_module}.categories.index")
            ->with('success', 'Category updated successfully.');
    }

    /**
     * Remove the specified resource from storage.
     */
    public function destroy(Category $category)
    {
        try {
            if ($category->type != $this->_type) {
                return redirect()
                    ->route('admin.pages.404')
                    ->with('error', 'Category not found.');
            }

            if (! $category->canDelete()) {
                return back()->withErrors(['error' => 'Cannot delete category that has articles or child categories.']);
            }

            $category->delete();

            return redirect()
                ->route("admin.{$this->_module}.categories.index")
                ->with('success', 'Category deleted successfully.');
        } catch (\Exception $e) {
            return back()->withErrors(['error' => $e->getMessage()]);
        }
    }

    /**
     * Restore a soft deleted category.
     */
    public function restore($id)
    {
        $category = Category::withTrashed()
            ->where('type', $this->_type)
            ->findOrFail($id);

        if (! $category) {
            return redirect()
                ->route('admin.pages.404')
                ->with('error', 'Category not found.');
        }

        $category->restore();

        return redirect()
            ->route("admin.{$this->_module}.categories.index")
            ->with('success', 'Category restored successfully.');
    }

    /**
     * Force delete a category.
     */
    public function forceDelete($id)
    {
        $category = Category::withTrashed()
            ->where('type', $this->_type)
            ->findOrFail($id);

        try {
            if (! $category) {
                return redirect()
                    ->route('admin.pages.404')
                    ->with('error', 'Category not found.');
            }

            if (! $category->canDelete()) {
                return back()->withErrors(['error' => 'Cannot permanently delete category that has articles or child categories.']);
            }

            $category->forceDelete();

            return redirect()
                ->route("admin.{$this->_module}.categories.index")
                ->with('success', 'Category permanently deleted.');
        } catch (\Exception $e) {
            return back()->withErrors(['error' => $e->getMessage()]);
        }
    }

    /**
     * Check if a category is a descendant of another category.
     */
    private function isDescendant(Category $category, Category $potentialParent): bool
    {
        $current = $potentialParent;

        while ($current && $current->parent_id !== 0) {
            if ($current->parent_id === $category->id) {
                return true;
            }
            $current = $current->parent;
        }

        return false;
    }

    /**
     * Sanitize slug to only allow a-z, 0-9, and hyphens
     *
     * @param string $slug
     * @return string
     */
    private function sanitizeSlug($slug)
    {
        if (empty($slug)) {
            return $slug;
        }

        return preg_replace('/^-+|-+$/', '', // Remove leading/trailing hyphens
            preg_replace('/-+/', '-', // Replace multiple consecutive hyphens with single hyphen
                preg_replace('/[^a-z0-9\s-]/', '', // Remove special characters except spaces and hyphens
                    strtolower(trim($slug))
                )
            )
        );
    }

    /**
     * Convert Japanese text to Romaji.
     *
     * @param string $string
     * @return string
     */
    private function convertJapaneseToRomaji($string, $default = 'category')
    {
        $romaji = null;
        $default = $default . '-' . uniqid();

        try {
            $kakasiOutput = shell_exec('echo ' . escapeshellarg($string) . ' | kakasi -i utf8 -Ha -Ka -Ja -Ea -ka 2>/dev/null');
            $romaji = trim($kakasiOutput);
        } catch (\Exception $e) {
            logger()->error('Failed to convert Japanese to Romaji: ' . $e->getMessage());
        }

        if (empty($romaji) || $romaji === ' ') {
            $romaji = transliterator_transliterate(
                'Any-Latin; NFKD; [:Nonspacing Mark:] Remove; Lower; NFC',
                $string
            );
        }

        if (empty($romaji)) {
            $romaji = $default;
        }

        if ($romaji === $default) {
            logger()->warning('Failed to convert Japanese to Romaji for string: ' . $string);
        }

        return $romaji;
    }
}
