<?php
declare(strict_types=1);

namespace App\Controller;

use Cake\Datasource\Exception\RecordNotFoundException;
use Cake\Http\Exception\ForbiddenException;

/**
 * Faqs Controller
 *
 * @property \App\Model\Table\FaqsTable $Faqs
 */
class FaqsController extends AppController
{
    /** helper: current user is super? */
    private function isSuper(): bool
    {
        $user = $this->request->getAttribute('identity');
        return (bool)($user && ($user->role === 'super'));
    }

    /**
     * Index
     */
    public function index()
    {
        $tag = trim((string)$this->request->getQuery('tag', ''));
        $q   = trim((string)$this->request->getQuery('q', ''));

        $query = $this->Faqs->find()
            ->orderDesc('Faqs.updated_at')
            ->orderDesc('Faqs.created_at');

        if ($tag !== '') {
            $query->where(["FIND_IN_SET(:t, REPLACE(LOWER(IFNULL(Faqs.tags,'')),' ','')) > 0"])
                ->bind(':t', strtolower($tag), 'string');
        }

        if ($q !== '') {
            $like = "%{$q}%";
            $query->where([
                'OR' => [
                    'Faqs.category LIKE' => $like,
                    'Faqs.question LIKE' => $like,
                    'Faqs.answer LIKE'   => $like,
                    'Faqs.link LIKE'     => $like,
                    'Faqs.tags LIKE'     => $like,
                ]
            ]);
        }

        $faqs = $this->paginate($query);
        $allTags = $this->getAllTags();

        $this->set(compact('faqs', 'allTags', 'tag', 'q'));
    }

    /** collect unique tags for filter */
    private function getAllTags(): array
    {
        $rows = $this->Faqs->find()
            ->select(['tags'])
            ->where(['tags IS NOT' => null, "tags !=" => ''])
            ->enableHydration(false)
            ->toArray();

        $set = [];
        foreach ($rows as $r) {
            $parts = explode(',', str_replace(' ', '', strtolower($r['tags'])));
            foreach ($parts as $p) {
                $p = trim($p);
                if ($p !== '') $set[$p] = $p;
            }
        }
        ksort($set);
        return $set;
    }

    /**
     * View method
     *
     * @param string|null $id Faq id.
     * @return \Cake\Http\Response|null|void Renders view
     * @throws \Cake\Datasource\Exception\RecordNotFoundException When record not found.
     */
    public function view($id = null)
    {
        $faq = $this->Faqs->get($id, contain: []);
        $this->set(compact('faq'));
    }

    /**
     * Add method
     *
     * @return \Cake\Http\Response|null|void Redirects on successful add, renders view otherwise.
     */
    public function add()
    {
        $faq = $this->Faqs->newEmptyEntity();
        $categories = $this->getAllCategories();
        $allTags    = $this->getAllTags();

        if ($this->request->is('post')) {
            $data = $this->request->getData();

            $catNew = trim((string)($data['category_new'] ?? ''));
            $catSel = trim((string)($data['category'] ?? ''));
            $data['category'] = $catNew !== '' ? $catNew : ($catSel !== '' ? $catSel : null);
            unset($data['category_new']);

            if (isset($data['tags']) && is_array($data['tags'])) {
                $data['tags'] = implode(',', $data['tags']);
            }
            $data['tags'] = $this->normalizeTags((string)($data['tags'] ?? ''));

            if ($this->isSuper()) {
                $faq = $this->Faqs->patchEntity($faq, $data);
                if ($this->Faqs->save($faq)) {
                    $this->Flash->success(__('Saved.'));
                    return $this->redirect(['action' => 'index']);
                }
                $this->Flash->error(__('Could not save.'));
            } else {
                $ChangeRequests = $this->fetchTable('ChangeRequests');
                $cr = $ChangeRequests->newEmptyEntity();
                $cr = $ChangeRequests->patchEntity($cr, [
                    'user_id' => $this->request->getAttribute('identity')->id ?? null,
                    'action'  => 'add',
                    'faq_id'  => null,
                    'payload' => json_encode($data, JSON_UNESCAPED_UNICODE),
                    'status'  => 'pending',
                ]);
                if ($ChangeRequests->save($cr)) {
                    $this->Flash->success(__('Submitted for approval.'));
                } else {
                    $this->Flash->error(__('Could not submit request.'));
                }
                return $this->redirect(['action' => 'index']);
            }
        }

        $this->set(compact('faq', 'categories', 'allTags'));
    }

    /**
     * Edit
     * - super: save directly
     * - admin: create change_requests (pending)
     */
    public function edit($id = null)
    {
        $faq = $this->Faqs->get($id, contain: []);
        $categories = $this->getAllCategories();
        $allTags    = $this->getAllTags();

        if ($this->request->is(['patch','post','put'])) {
            $data = $this->request->getData();

            $catNew = trim((string)($data['category_new'] ?? ''));
            $catSel = trim((string)($data['category'] ?? ''));
            $data['category'] = $catNew !== '' ? $catNew : ($catSel !== '' ? $catSel : null);
            unset($data['category_new']);

            if (isset($data['tags']) && is_array($data['tags'])) {
                $data['tags'] = implode(',', $data['tags']);
            }
            $data['tags'] = $this->normalizeTags((string)($data['tags'] ?? ''));

            if ($this->isSuper()) {
                $faq = $this->Faqs->patchEntity($faq, $data);
                if ($this->Faqs->save($faq)) {
                    $this->Flash->success(__('Saved.'));
                    return $this->redirect(['action' => 'index']);
                }
                $this->Flash->error(__('Could not save.'));
            } else {
                $this->ChangeRequests = $this->fetchTable('ChangeRequests');

                $cr = $this->ChangeRequests->newEmptyEntity();
                $cr = $this->ChangeRequests->patchEntity($cr, [
                    'user_id' => $this->request->getAttribute('identity')->id ?? null,
                    'action'  => 'edit',
                    'faq_id'  => $faq->id,
                    'payload' => json_encode($data, JSON_UNESCAPED_UNICODE),
                    'status'  => 'pending',
                ]);
                if ($this->ChangeRequests->save($cr)) {
                    $this->Flash->success(__('Edit request submitted for approval.'));
                } else {
                    $this->Flash->error(__('Could not submit request.'));
                }
                return $this->redirect(['action' => 'index']);
            }
        }

        $this->set(compact('faq', 'categories', 'allTags'));
    }

    /**
     * Delete
     */
    public function delete($id = null)
    {
        $this->request->allowMethod(['post', 'delete']);

        if (!$this->isSuper()) {
            throw new ForbiddenException('Only super admin can delete.');
        }

        $faq = $this->Faqs->get($id);
        if ($this->Faqs->delete($faq)) {
            $this->Flash->success(__('The faq has been deleted.'));
        } else {
            $this->Flash->error(__('The faq could not be deleted. Please, try again.'));
        }

        return $this->redirect(['action' => 'index']);
    }

    public function approved()
    {
        if (!$this->isSuper()) {
            throw new ForbiddenException('Only super admin can access approvals.');
        }

        $this->ChangeRequests = $this->fetchTable('ChangeRequests');
        $query = $this->ChangeRequests->find()
            ->contain(['Users', 'Faqs'])
            ->orderDesc('ChangeRequests.created');

        $requests = $this->paginate($query);
        $this->set(compact('requests'));
    }

    /** approve request (POST, super only) */
    public function approve($id)
    {
        if (!$this->isSuper()) {
            throw new ForbiddenException();
        }
        $this->request->allowMethod(['post']);

        $this->ChangeRequests = $this->fetchTable('ChangeRequests');

        $cr = $this->ChangeRequests->get($id);

        if ($cr->status !== 'pending') {
            $this->Flash->error(__('Already processed.'));
            return $this->redirect(['action' => 'approved']);
        }

        $payload = json_decode((string)$cr->payload, true) ?: [];

        if ($cr->action === 'add') {
            $faq = $this->Faqs->newEmptyEntity();
            $this->Faqs->patchEntity($faq, $payload);
            $ok = (bool)$this->Faqs->save($faq);
        } else { // edit
            $faq = $this->Faqs->get((int)$cr->faq_id);
            $this->Faqs->patchEntity($faq, $payload);
            $ok = (bool)$this->Faqs->save($faq);
        }

        if ($ok) {
            $cr->status = 'approved';
            $cr->reviewed_by = $this->request->getAttribute('identity')->id ?? null;
            $cr->reviewed_at = date('Y-m-d H:i:s');
            $this->ChangeRequests->save($cr);
            $this->Flash->success(__('Approved and applied.'));
        } else {
            $this->Flash->error(__('Apply failed. Please check validation.'));
        }

        return $this->redirect(['action' => 'approved']);
    }

    /** reject request (POST, super only) */
    public function reject($id)
    {
        if (!$this->isSuper()) {
            throw new ForbiddenException();
        }
        $this->request->allowMethod(['post']);

        $this->ChangeRequests = $this->fetchTable('ChangeRequests');

        $cr = $this->ChangeRequests->get($id);

        if ($cr->status !== 'pending') {
            $this->Flash->error(__('Already processed.'));
            return $this->redirect(['action' => 'approved']);
        }

        $cr->status = 'rejected';
        $cr->reviewed_by = $this->request->getAttribute('identity')->id ?? null;
        $cr->reviewed_at = date('Y-m-d H:i:s');
        $this->ChangeRequests->save($cr);

        $this->Flash->success(__('Rejected.'));
        return $this->redirect(['action' => 'approved']);
    }

    public function getPendingCount(): int
    {
        if (!$this->isSuper()) return 0;
        $ChangeRequests = $this->fetchTable('ChangeRequests');
        return $this->ChangeRequests->find()
            ->where(['status' => 'pending'])
            ->count();
    }

    public function myRequests()
    {
        $user = $this->request->getAttribute('identity');
        if (!$user) {
            return $this->redirect(['controller'=>'Users','action'=>'login']);
        }

        $ChangeRequests = $this->fetchTable('ChangeRequests');

        $query = $ChangeRequests->find()
            ->where(['ChangeRequests.user_id' => $user->id])
            ->contain(['Faqs'])
            ->orderDesc('ChangeRequests.created');

        $this->paginate = ['limit' => 20];
        $requests = $this->paginate($query);

        $this->set(compact('requests'));
        $this->viewBuilder()->setTemplate('my_requests');
    }

    /** utils */
    private function normalizeTags(string $tags): string
    {
        if ($tags === '') return '';
        $arr = array_filter(array_unique(array_map(
            fn($t) => strtolower(trim($t)),
            explode(',', $tags)
        )));
        return implode(',', $arr);
    }

    private function getAllCategories(): array
    {
        $rows = $this->Faqs->find()
            ->select(['category'])
            ->where(['category IS NOT' => null, "category !=" => ''])
            ->distinct()
            ->orderAsc('category')
            ->enableHydration(false)
            ->toArray();

        $options = [];
        foreach ($rows as $r) {
            $c = trim((string)$r['category']);
            if ($c !== '') $options[$c] = $c;
        }
        return $options;
    }
}
