<?php

namespace App\Controllers\Api\V1;

use App\Controllers\BaseController;
use App\Services\Interview\InterviewService;

class InterviewController extends BaseController
{
    private InterviewService $interviews;

    public function initController(\CodeIgniter\HTTP\RequestInterface $request, \CodeIgniter\HTTP\ResponseInterface $response, \Psr\Log\LoggerInterface $logger)
    {
        parent::initController($request, $response, $logger);
        $this->interviews = new InterviewService();
    }

    public function schedule(int $applicationId)
    {
        $user = $this->currentUser();
        $input = $this->request->getJSON(true);

        $rules = [
            'mode'         => 'required|in_list[zoom,google_meet,onsite,phone]',
            'scheduled_at' => 'required|valid_date',
        ];
        if (! $this->validate($rules)) {
            return $this->jsonValidationError($this->validator->getErrors());
        }

        try {
            $interview = $this->interviews->schedule(
                $applicationId,
                $user->id,
                $input['mode'],
                $input['scheduled_at'],
                (int) ($input['duration_minutes'] ?? 30),
                $input['location'] ?? null
            );
        } catch (\InvalidArgumentException | \RuntimeException $e) {
            return $this->jsonError([['code' => 'SCHEDULE_FAILED', 'message' => $e->getMessage()]], 422);
        }

        return $this->jsonSuccess(['interview' => $interview], [], 201);
    }

    public function reschedule(int $interviewId)
    {
        $user = $this->currentUser();
        $input = $this->request->getJSON(true);

        if (empty($input['scheduled_at'])) {
            return $this->jsonValidationError(['scheduled_at' => 'scheduled_at is required.']);
        }

        try {
            $interview = $this->interviews->reschedule($interviewId, $input['scheduled_at'], $user->id);
        } catch (\RuntimeException $e) {
            return $this->jsonError([['code' => 'NOT_FOUND', 'message' => $e->getMessage()]], 404);
        }

        return $this->jsonSuccess(['interview' => $interview]);
    }

    public function cancel(int $interviewId)
    {
        $user = $this->currentUser();

        try {
            $this->interviews->cancel($interviewId, $user->id);
        } catch (\RuntimeException $e) {
            return $this->jsonError([['code' => 'NOT_FOUND', 'message' => $e->getMessage()]], 404);
        }

        return $this->jsonSuccess(['message' => 'Interview cancelled.']);
    }

    public function submitFeedback(int $interviewId)
    {
        $user = $this->currentUser();
        $input = $this->request->getJSON(true);

        $rules = ['feedback' => 'required', 'rating' => 'required|is_natural_no_zero'];
        if (! $this->validate($rules)) {
            return $this->jsonValidationError($this->validator->getErrors());
        }

        try {
            $interview = $this->interviews->submitFeedback($interviewId, $input['feedback'], (int) $input['rating'], $user->id);
        } catch (\InvalidArgumentException $e) {
            return $this->jsonError([['code' => 'INVALID_RATING', 'message' => $e->getMessage()]], 422);
        }

        return $this->jsonSuccess(['interview' => $interview]);
    }

    /**
     * The full CRM-style activity timeline for one interview: notes,
     * auto-logged status changes, and file-added markers. Visible to the
     * whole agency team (owner + recruiters) AND the candidate — so
     * everyone who touches this interview has the same shared history,
     * not just whoever scheduled it.
     */
    public function timeline(int $interviewId)
    {
        if (! $this->canAccessInterview($interviewId)) {
            return $this->jsonError([['code' => 'FORBIDDEN', 'message' => 'You do not have access to this interview.']], 403);
        }

        $user = $this->currentUser();
        $timeline = $this->interviews->timeline($interviewId);

        // Internal notes stay internal to the agency team — a candidate
        // viewing their own interview history sees status changes and
        // files, not the agency's private commentary about them.
        if ($user->isCandidate()) {
            $timeline = array_values(array_filter($timeline, fn ($t) => $t['update_type'] !== 'note'));
        }

        return $this->jsonSuccess([
            'timeline' => $timeline,
            'files'    => $this->interviews->listFiles($interviewId),
        ]);
    }

    /**
     * Manual note — agency side only (owner/recruiter); candidates can
     * read the timeline but don't post internal notes into it.
     */
    public function addNote(int $interviewId)
    {
        $user = $this->currentUser();
        if ($user->isCandidate() || ! $this->canAccessInterview($interviewId)) {
            return $this->jsonError([['code' => 'FORBIDDEN', 'message' => 'You do not have access to this interview.']], 403);
        }

        $input = $this->request->getJSON(true);
        if (empty($input['content'])) {
            return $this->jsonValidationError(['content' => 'Note content is required.']);
        }

        try {
            $update = $this->interviews->addNote($interviewId, $user->id, $input['content']);
        } catch (\RuntimeException $e) {
            return $this->jsonError([['code' => 'NOT_FOUND', 'message' => $e->getMessage()]], 404);
        }

        return $this->jsonSuccess(['update' => $update], [], 201);
    }

    /**
     * File upload — agency side only. Candidates can VIEW/download
     * attached files (they may need the assessment doc, signed offer,
     * etc.) but don't upload into an agency's interview record.
     */
    public function uploadFile(int $interviewId)
    {
        $user = $this->currentUser();
        if ($user->isCandidate() || ! $this->canAccessInterview($interviewId)) {
            return $this->jsonError([['code' => 'FORBIDDEN', 'message' => 'You do not have access to this interview.']], 403);
        }

        $file = $this->request->getFile('file');
        if (! $file) {
            return $this->jsonError([['code' => 'FILE_MISSING', 'message' => 'No file uploaded under field "file".']], 400);
        }

        try {
            $uploader = new \App\Services\Interview\InterviewFileUploadService();
            $stored = $uploader->validateAndStore($file);
        } catch (\InvalidArgumentException $e) {
            return $this->jsonError([['code' => 'INVALID_FILE', 'message' => $e->getMessage()]], 422);
        } catch (\RuntimeException $e) {
            return $this->jsonError([['code' => 'UPLOAD_FAILED', 'message' => $e->getMessage()]], 500);
        }

        try {
            $record = $this->interviews->addFile($interviewId, $user->id, $stored['file_path'], $stored['original_filename'], $stored['file_size_kb']);
        } catch (\RuntimeException $e) {
            return $this->jsonError([['code' => 'NOT_FOUND', 'message' => $e->getMessage()]], 404);
        }

        return $this->jsonSuccess(['file' => $record], [], 201);
    }

    public function downloadFile(int $fileId)
    {
        $fileModel = new \App\Models\InterviewFileModel();
        $fileRecord = $fileModel->find($fileId);
        if (! $fileRecord) {
            return $this->jsonError([['code' => 'NOT_FOUND', 'message' => 'File not found.']], 404);
        }

        if (! $this->canAccessInterview($fileRecord['interview_id'])) {
            return $this->jsonError([['code' => 'FORBIDDEN', 'message' => 'You do not have access to this file.']], 403);
        }

        $absolutePath = (new \App\Services\Interview\InterviewFileUploadService())->absolutePath($fileRecord['file_path']);
        if (! is_file($absolutePath)) {
            return $this->jsonError([['code' => 'FILE_MISSING', 'message' => 'The file is no longer available on disk.']], 404);
        }

        return $this->response->download($absolutePath, null)->setFileName($fileRecord['original_filename'] ?: 'file');
    }

    /**
     * True if the current user is either (a) an agency user whose agency
     * owns the job this interview is for, or (b) the candidate this
     * interview is with.
     */
    private function canAccessInterview(int $interviewId): bool
    {
        $user = $this->currentUser();
        $db = \Config\Database::connect();

        $row = $db->table('interviews i')
            ->select('j.agency_id, cp.user_id as candidate_user_id')
            ->join('job_applications ja', 'ja.id = i.job_application_id')
            ->join('jobs j', 'j.id = ja.job_id')
            ->join('candidate_profiles cp', 'cp.id = ja.candidate_profile_id')
            ->where('i.id', $interviewId)
            ->get()->getRowArray();

        if (! $row) {
            return false;
        }

        if ($user->isCandidate()) {
            return (int) $row['candidate_user_id'] === $user->id;
        }

        return $user->agency_id && (int) $row['agency_id'] === (int) $user->agency_id;
    }

    public function myUpcoming()
    {
        $user = $this->currentUser();
        return $this->jsonSuccess(['interviews' => $this->interviews->upcomingForCandidate($user->id)]);
    }

    public function forApplication(int $applicationId)
    {
        return $this->jsonSuccess(['interviews' => $this->interviews->forApplication($applicationId)]);
    }

    /**
     * Lists all upcoming interviews across the agency's applications —
     * previously only per-application scheduling/listing existed, with no
     * way for a recruiter to see "everything on my calendar" at a glance.
     */
    public function agencyUpcoming()
    {
        $user = $this->currentUser();
        $interviews = \Config\Database::connect()->table('interviews i')
            ->select('i.*, ja.job_id, j.title as job_title, u.name as candidate_name')
            ->join('job_applications ja', 'ja.id = i.job_application_id')
            ->join('jobs j', 'j.id = ja.job_id')
            ->join('candidate_profiles cp', 'cp.id = ja.candidate_profile_id')
            ->join('users u', 'u.id = cp.user_id')
            ->where('j.agency_id', $user->agency_id)
            ->where('i.status !=', 'cancelled')
            ->orderBy('i.scheduled_at', 'ASC')
            ->get()->getResultArray();

        return $this->jsonSuccess(['interviews' => $interviews]);
    }
}
