<?php
declare(strict_types=1);

namespace App\Controllers;

use App\Core\Controller;
use App\Models\Submission;
use App\Models\Conference;
use App\Models\Track;
use App\Models\DecisionModel;
use App\Models\User;
use App\Models\CustomFieldModel;
use App\Services\NotificationService;
use App\Services\ConferenceNotificationService;
use App\Services\CustomFieldService;

final class SubmissionController extends Controller
{
    public function index(): void
    {
        require_login();

        $submissionModel = new Submission();

        if (has_permission('submissions.view_all')) {
            $submissions = $submissionModel->all();
        } else {
            require_permission('submissions.view_own');
            $submissions = $submissionModel->allByAuthor((int) auth_id());
        }

        $this->view('submissions/index', [
            'submissions' => $submissions,
        ]);
    }

    public function create(): void
    {
        require_permission('submissions.create');

        $conferenceModel = new Conference();
        $trackModel = new Track();
        $submissionModel = new Submission();
        $cfModel = new CustomFieldModel();

        $conferenceId = (int) ($_GET['conference_id'] ?? ($_POST['conference_id'] ?? 0));
        $conferences = $conferenceModel->openForSubmission();
        $tracks = $conferenceId > 0 ? $trackModel->activeByConference($conferenceId) : [];
        $submissionTypes = $conferenceId > 0 ? $conferenceModel->submissionTypesByConference($conferenceId) : [];
        $cfFields = $cfModel->activeByModule('submission');

        if ($this->isPost()) {
            csrf_check();

            $conferenceId = (int) ($_POST['conference_id'] ?? 0);
            $trackId = (int) ($_POST['track_id'] ?? 0);
            $submissionTypeId = (int) ($_POST['submission_type_id'] ?? 0);
            $title = trim((string) ($_POST['title'] ?? ''));
            $titleAr = trim((string) ($_POST['title_ar'] ?? ''));
            $abstract = trim((string) ($_POST['abstract'] ?? ''));
            $abstractAr = trim((string) ($_POST['abstract_ar'] ?? ''));
            $keywords = trim((string) ($_POST['keywords'] ?? ''));
            $keywordsAr = trim((string) ($_POST['keywords_ar'] ?? ''));
            $cfInput = $_POST['cf'] ?? [];

            set_old($_POST);

            if ($conferenceId <= 0 || $trackId <= 0 || $title === '' || $titleAr === '' || $abstract === '' || $abstractAr === '' || $keywords === '' || $keywordsAr === '') {
                flash('danger', trans('required_fields'));
                redirect('submissions/create');
            }

            $cfErrors = CustomFieldService::validate($cfFields, is_array($cfInput) ? $cfInput : []);
            if (!empty($cfErrors)) {
                flash('danger', trans('required_fields'));
                redirect('submissions/create');
            }

            if ($conferenceModel->submissionDeadlinePassed($conferenceId)) {
                flash('danger', trans('conference_deadline_passed'));
                redirect('submissions');
            }

            try {
                $upload = handle_uploaded_submission_file($_FILES['main_file'] ?? []);
            } catch (\Throwable $e) {
                flash('danger', $e->getMessage());
                redirect('submissions/create?conference_id=' . $conferenceId);
            }

            $submissionNo = generate_submission_no($conferenceId);

            $newId = $submissionModel->create([
                'conference_id' => $conferenceId,
                'track_id' => $trackId,
                'submission_type_id' => $submissionTypeId ?: null,
                'author_id' => (int) auth_id(),
                'submission_no' => $submissionNo,
                'title' => $title,
                'title_ar' => $titleAr,
                'abstract' => $abstract,
                'abstract_ar' => $abstractAr,
                'keywords' => $keywords,
                'keywords_ar' => $keywordsAr,
                'file_path' => $upload['relative_path'],
                'original_filename' => $upload['original_filename'],
                'response_letter_path' => null,
                'status' => 'submitted',
                'current_version' => 1,
                'submitted_at' => date('Y-m-d H:i:s'),
                'updated_by' => (int) auth_id(),
            ]);

                $this->submissionModel->addSubmissionFile($submissionId, $fileCategory, $fileData['relative_path'], $fileData['original_filename'], auth_id());

                try {
                    $queue = new \App\Services\MailQueueService();
                    $tpl = $queue->templateByKey('submission_ack');
                    if ($tpl && !empty($_SESSION['user']['email'])) {
                        $lang = current_lang();
                        $rendered = $queue->renderTemplate($tpl, [
                            'name' => trim((string) (($_SESSION['user']['first_name'] ?? '') . ' ' . ($_SESSION['user']['last_name'] ?? ''))),
                            'submission_no' => (string) generate_submission_no((int) ($_POST['conference_id'] ?? 0)),
                        ], $lang);
                        $queue->queue((string) $_SESSION['user']['email'], $rendered['subject'], $rendered['body'], 'submission_ack');
                    }
                } catch (\Throwable $e) {
                }

                $submissionFilesInput = $_POST['extra_file_category'] ?? [];
                if (!empty($_FILES['extra_file']['name']) && is_array($_FILES['extra_file']['name'])) {
                    foreach ($_FILES['extra_file']['name'] as $idx => $name) {
                        if (!$name) {
                            continue;
                        }
                        $single = [
                            'name' => $_FILES['extra_file']['name'][$idx],
                            'type' => $_FILES['extra_file']['type'][$idx],
                            'tmp_name' => $_FILES['extra_file']['tmp_name'][$idx],
                            'error' => $_FILES['extra_file']['error'][$idx],
                            'size' => $_FILES['extra_file']['size'][$idx],
                        ];
                        $extraUpload = handle_uploaded_submission_file($single, 'submissions/files');
                        $extraCategory = isset($submissionFilesInput[$idx]) ? (string) $submissionFilesInput[$idx] : 'other';
                        $this->submissionModel->addSubmissionFile($submissionId, $extraCategory, $extraUpload['relative_path'], $extraUpload['original_filename'], auth_id());
                    }
                }

            $authors = $this->collectAuthorsFromPost();
            if (empty($authors)) {
                $user = auth_user();
                $authors[] = [
                    'full_name' => trim((string) (($user['first_name'] ?? '') . ' ' . ($user['last_name'] ?? ''))),
                    'email' => (string) ($user['email'] ?? ''),
                    'affiliation' => '',
                    'country' => '',
                    'is_corresponding' => 1,
                ];
            }

            $submissionModel->replaceAuthors($newId, $authors);
            $submissionModel->addVersion(
                $newId,
                1,
                $upload['relative_path'],
                $upload['original_filename'],
                (int) auth_id(),
                'Initial submission'
            );

            $cfModel->saveValues('submission', $newId, $cfFields, is_array($cfInput) ? $cfInput : []);

            $author = (new User())->findById((int) auth_id());
            $submission = $submissionModel->findById($newId);

            if ($author && $submission) {
                ConferenceNotificationService::notifyAndEmail(
                    $author,
                    (int) $submission['conference_id'],
                    'submission_created',
                    [
                        'user_name' => trim((string) $author['first_name'] . ' ' . (string) $author['last_name']),
                        'submission_no' => (string) $submission['submission_no'],
                        'submission_title' => (string) $submission['title'],
                        'conference_title' => current_lang() === 'ar' ? (string) $submission['title_ar'] : (string) $submission['title_en'],
                    ],
                    'submission_created',
                    'submissions',
                    $newId
                );
            }

            audit_log('create', 'submissions', $newId, ['submission_no' => $submissionNo]);

            clear_old();
            flash('success', trans('submission_created_success'));
            redirect('submissions/show?id=' . $newId);
        }

        $this->view('submissions/create', [
            'conferences' => $conferences,
            'tracks' => $tracks,
            'submissionTypes' => $submissionTypes,
            'cfFields' => $cfFields,
            'cfValues' => [],
        ]);
    }

    public function edit(): void
    {
        require_permission('submissions.edit_own');

        $id = (int) ($_GET['id'] ?? 0);
        if ($id <= 0) {
            redirect('submissions');
        }

        $submissionModel = new Submission();
        $conferenceModel = new Conference();
        $trackModel = new Track();
        $cfModel = new CustomFieldModel();

        $submission = $submissionModel->findByIdForAuthor($id, (int) auth_id());
        if (!$submission) {
            redirect('submissions');
        }

        $deadlinePassed = $conferenceModel->submissionDeadlinePassed((int) $submission['conference_id']);
        if (!$submissionModel->editableByAuthor($submission, $deadlinePassed)) {
            flash('danger', trans('conference_deadline_passed'));
            redirect('submissions/show?id=' . $id);
        }

        $cfFields = $cfModel->activeByModule('submission');

        if ($this->isPost()) {
            csrf_check();

            $conferenceId = (int) ($_POST['conference_id'] ?? 0);
            $trackId = (int) ($_POST['track_id'] ?? 0);
            $submissionTypeId = (int) ($_POST['submission_type_id'] ?? 0);
            $title = trim((string) ($_POST['title'] ?? ''));
            $titleAr = trim((string) ($_POST['title_ar'] ?? ''));
            $abstract = trim((string) ($_POST['abstract'] ?? ''));
            $abstractAr = trim((string) ($_POST['abstract_ar'] ?? ''));
            $keywords = trim((string) ($_POST['keywords'] ?? ''));
            $keywordsAr = trim((string) ($_POST['keywords_ar'] ?? ''));
            $cfInput = $_POST['cf'] ?? [];

            if ($conferenceId <= 0 || $trackId <= 0 || $title === '' || $titleAr === '' || $abstract === '' || $abstractAr === '' || $keywords === '' || $keywordsAr === '') {
                flash('danger', trans('required_fields'));
                redirect('submissions/edit?id=' . $id);
            }

            $cfErrors = CustomFieldService::validate($cfFields, is_array($cfInput) ? $cfInput : []);
            if (!empty($cfErrors)) {
                flash('danger', trans('required_fields'));
                redirect('submissions/edit?id=' . $id);
            }

            $filePath = (string) ($submission['file_path'] ?? '');
            $originalFilename = (string) ($submission['original_filename'] ?? '');
            $newVersion = (int) $submission['current_version'];

            if (!empty($_FILES['main_file']['name'])) {
                try {
                    $upload = handle_uploaded_submission_file($_FILES['main_file'] ?? []);
                    $filePath = $upload['relative_path'];
                    $originalFilename = $upload['original_filename'];
                    $newVersion++;
                } catch (\Throwable $e) {
                    flash('danger', $e->getMessage());
                    redirect('submissions/edit?id=' . $id);
                }
            }

            $submissionModel->update($id, [
                'conference_id' => $conferenceId,
                'track_id' => $trackId,
                'submission_type_id' => $submissionTypeId ?: null,
                'title' => $title,
                'title_ar' => $titleAr,
                'abstract' => $abstract,
                'abstract_ar' => $abstractAr,
                'keywords' => $keywords,
                'keywords_ar' => $keywordsAr,
                'file_path' => $filePath,
                'original_filename' => $originalFilename,
                'response_letter_path' => $submission['response_letter_path'] ?? null,
                'status' => (string) $submission['status'],
                'current_version' => $newVersion,
                'updated_by' => (int) auth_id(),
            ]);

            if ($newVersion > (int) $submission['current_version']) {
                $submissionModel->addVersion(
                    $id,
                    $newVersion,
                    $filePath,
                    $originalFilename,
                    (int) auth_id(),
                    'Updated submission'
                );
            }

            $authors = $this->collectAuthorsFromPost();
            $submissionModel->replaceAuthors($id, $authors);
            $cfModel->saveValues('submission', $id, $cfFields, is_array($cfInput) ? $cfInput : []);

            audit_log('update', 'submissions', $id, ['current_version' => $newVersion]);

            flash('success', trans('submission_updated_success'));
            redirect('submissions/show?id=' . $id);
        }

        $authors = $submissionModel->authors($id);
        $versions = $submissionModel->versions($id);

        $this->view('submissions/edit', [
            'submission' => $submission,
            'authors' => $authors,
            'files' => $files,
            'versions' => $versions,
            'conferences' => $conferenceModel->openForSubmission(),
            'tracks' => $trackModel->activeByConference((int) $submission['conference_id']),
            'submissionTypes' => $conferenceModel->submissionTypesByConference((int) $submission['conference_id']),
            'cfFields' => $cfFields,
            'cfValues' => $cfModel->valuesForEntity('submission', $id),
        ]);
    }

    public function show(): void
    {
        require_login();

        $id = (int) ($_GET['id'] ?? 0);
        if ($id <= 0) {
            redirect('submissions');
        }

        $submissionModel = new Submission();
        $decisionModel = new DecisionModel();
        $cfModel = new CustomFieldModel();

        if (has_permission('submissions.view_all')) {
            $submission = $submissionModel->findById($id);
        } else {
            $submission = $submissionModel->findByIdForAuthor($id, (int) auth_id());
        }

        if (!$submission) {
            redirect('submissions');
        }

        $files = $this->submissionModel->filesBySubmission($id);

        $this->view('submissions/show', [
            'submission' => $submission,
            'authors' => $submissionModel->authors($id),
            'versions' => $submissionModel->versions($id),
            'decision' => $decisionModel->getBySubmission($id),
            'decisionHistory' => $decisionModel->historyBySubmission($id),
            'cfFields' => $cfModel->activeByModule('submission'),
            'cfValues' => $cfModel->valuesForEntity('submission', $id),
        ]);
    }

    public function uploadRevision(): void
    {
        require_permission('submissions.upload_revision_own');

        $id = (int) ($_GET['id'] ?? 0);
        if ($id <= 0) {
            redirect('submissions');
        }

        $submissionModel = new Submission();
        $decisionModel = new DecisionModel();

        $submission = $submissionModel->findByIdForAuthor($id, (int) auth_id());
        if (!$submission) {
            redirect('submissions');
        }

        if (!$submissionModel->canUploadRevision($submission)) {
            flash('danger', trans('cannot_upload_revision'));
            redirect('submissions/show?id=' . $id);
        }

        if ($this->isPost()) {
            csrf_check();

            try {
                $upload = handle_uploaded_submission_file($_FILES['main_file'] ?? [], 'submissions/revisions');
            } catch (\Throwable $e) {
                flash('danger', $e->getMessage());
                redirect('submissions/revision?id=' . $id);
            }

            $responsePath = (string) ($submission['response_letter_path'] ?? '');
            if (!empty($_FILES['response_letter']['name'])) {
                try {
                    $resp = handle_uploaded_response_letter($_FILES['response_letter'] ?? [], 'submissions/response');
                    $responsePath = $resp['relative_path'];
                } catch (\Throwable $e) {
                    flash('danger', $e->getMessage());
                    redirect('submissions/revision?id=' . $id);
                }
            }

            $newVersion = ((int) $submission['current_version']) + 1;

            $submissionModel->applyRevisionUpload(
                $id,
                $upload['relative_path'],
                $upload['original_filename'],
                $responsePath ?: null,
                $newVersion,
                (int) auth_id()
            );

            $submissionModel->addVersion(
                $id,
                $newVersion,
                $upload['relative_path'],
                $upload['original_filename'],
                (int) auth_id(),
                'Revision uploaded by author'
            );

            $updated = $submissionModel->findById($id);
            if ($updated) {
                $chairs = (new User())->conferenceChairs();
                foreach ($chairs as $chair) {
                    ConferenceNotificationService::notifyAndEmail(
                        $chair,
                        (int) $updated['conference_id'],
                        'revision_uploaded',
                        [
                            'user_name' => trim((string) $chair['first_name'] . ' ' . (string) $chair['last_name']),
                            'submission_no' => (string) $updated['submission_no'],
                            'submission_title' => (string) $updated['title'],
                            'conference_title' => current_lang() === 'ar' ? (string) $updated['title_ar'] : (string) $updated['title_en'],
                        ],
                        'revision_uploaded',
                        'submissions',
                        $id
                    );
                }
            }

            audit_log('upload_revision', 'submissions', $id, ['version' => $newVersion]);

            flash('success', trans('revision_uploaded_success'));
            redirect('submissions/show?id=' . $id);
        }

        $this->view('submissions/revision', [
            'submission' => $submission,
            'decision' => $decisionModel->getBySubmission($id),
        ]);
    }

    public function download(): void
    {
        require_login();

        $type = trim((string) ($_GET['type'] ?? 'submission'));
        $id = (int) ($_GET['id'] ?? 0);

        if ($id <= 0) {
            http_response_code(404);
            exit(trans('file_not_found'));
        }

        $submissionModel = new Submission();

        if (has_permission('submissions.view_all')) {
            $submission = $submissionModel->findById($id);
        } else {
            $submission = $submissionModel->findByIdForAuthor($id, (int) auth_id());
        }

        if (!$submission) {
            http_response_code(404);
            exit(trans('file_not_found'));
        }

        if ($type === 'response' && !empty($submission['response_letter_path'])) {
            download_storage_file((string) $submission['response_letter_path'], 'response-letter-' . $submission['submission_no']);
        }

        download_storage_file((string) $submission['file_path'], (string) ($submission['original_filename'] ?? ('submission-' . $submission['submission_no'])));
    }

    private function collectAuthorsFromPost(): array
    {
        $names = $_POST['author_name'] ?? [];
        $emails = $_POST['author_email'] ?? [];
        $affiliations = $_POST['author_affiliation'] ?? [];
        $countries = $_POST['author_country'] ?? [];
        $corresponding = $_POST['author_corresponding'] ?? [];

        $authors = [];
        $count = max(count((array) $names), count((array) $emails));

        for ($i = 0; $i < $count; $i++) {
            $authors[] = [
                'full_name' => trim((string) ($names[$i] ?? '')),
                'email' => trim((string) ($emails[$i] ?? '')),
                'affiliation' => trim((string) ($affiliations[$i] ?? '')),
                'country' => trim((string) ($countries[$i] ?? '')),
                'is_corresponding' => isset($corresponding[$i]) ? 1 : 0,
            ];
        }

        return array_values(array_filter($authors, fn($a) => $a['full_name'] !== ''));
    }
}
