<?php

namespace Modules\Operations\Controllers;

use App\Controllers\BaseController;
use App\Models\CompanyModel;
use App\Models\EmployeeModel;
use App\Models\FsCustomerSignatureModel;
use App\Models\FsInspectionReportModel;
use App\Models\FsServiceVisitModel;
use App\Models\FsTechnicianJobModel;
use App\Models\WorkOrderModel;
use Modules\Operations\Services\FieldServiceService;

class FieldServiceController extends BaseController
{
    protected FsServiceVisitModel $visits;
    protected FsTechnicianJobModel $jobs;
    protected FsInspectionReportModel $reports;
    protected FsCustomerSignatureModel $signatures;
    protected FieldServiceService $service;
    protected EmployeeModel $employees;
    protected CompanyModel $companies;
    protected WorkOrderModel $workOrders;

    public function initController(\CodeIgniter\HTTP\RequestInterface $request, \CodeIgniter\HTTP\ResponseInterface $response, \Psr\Log\LoggerInterface $logger)
    {
        parent::initController($request, $response, $logger);
        $this->visits     = new FsServiceVisitModel();
        $this->jobs       = new FsTechnicianJobModel();
        $this->reports    = new FsInspectionReportModel();
        $this->signatures = new FsCustomerSignatureModel();
        $this->service    = new FieldServiceService();
        $this->employees  = new EmployeeModel();
        $this->companies  = new CompanyModel();
        $this->workOrders = new WorkOrderModel();
    }

    // ------------------------------------------------------------------ Visits

    public function indexVisits()
    {
        $search   = trim((string) $this->request->getGet('q'));
        $status   = $this->request->getGet('status') ?: null;
        $priority = $this->request->getGet('priority') ?: null;

        $entries = $this->visits
            ->scopeForList($search ?: null, $status, $priority)
            ->orderBy('fs_service_visits.scheduled_date', 'ASC')
            ->orderBy('fs_service_visits.id', 'DESC')
            ->paginate(25);

        return view('Modules\Operations\Views\field_service\service_visits\index', array_merge($this->baseViewData(), [
            'title'   => 'Service Visits',
            'entries' => $entries,
            'pager'   => $this->visits->pager,
            'filters' => compact('search', 'status', 'priority'),
        ]));
    }

    public function createVisitForm()
    {
        return view('Modules\Operations\Views\field_service\service_visits\create', array_merge($this->baseViewData(), [
            'title'      => 'Create Service Visit',
            'visit'      => [],
            'employees'  => $this->activeEmployees(),
            'companies'  => $this->companies->getActive(),
            'workOrders' => $this->workOrders->getAssignable(),
        ]));
    }

    public function storeVisit()
    {
        if (! $this->validate([
            'title'                  => 'required|min_length[2]|max_length[200]',
            'priority'               => 'permit_empty|in_list[low,normal,high,urgent]',
            'status'                 => 'permit_empty|in_list[draft,scheduled]',
            'company_id'             => 'permit_empty|is_natural_no_zero',
            'work_order_id'          => 'permit_empty|is_natural_no_zero',
            'technician_employee_id' => 'permit_empty|is_natural_no_zero',
            'scheduled_date'         => 'permit_empty|valid_date',
            'location'               => 'permit_empty|max_length[200]',
        ])) {
            return redirect()->back()->withInput()->with('errors', $this->validator->getErrors());
        }

        $result = $this->service->createVisit($this->request->getPost(), (int) $this->session->get('user_id'));
        if (! $result['success']) {
            return redirect()->back()->withInput()->with('errors', $result['errors']);
        }

        return redirect()->to('/operations/field-service/service-visits/' . $result['id'])
            ->with('success', 'Service visit created.');
    }

    public function showVisit(int $id)
    {
        $visit = $this->visits->scopeForList()->where('fs_service_visits.id', $id)->first();
        if (! $visit) {
            return redirect()->to('/operations/field-service/service-visits')->with('error', 'Service visit not found.');
        }

        return view('Modules\Operations\Views\field_service\service_visits\show', array_merge($this->baseViewData(), [
            'title' => $visit['number'],
            'visit' => $visit,
        ]));
    }

    public function editVisitForm(int $id)
    {
        $visit = $this->visits->find($id);
        if (! $visit) {
            return redirect()->to('/operations/field-service/service-visits')->with('error', 'Service visit not found.');
        }

        if (! in_array($visit['status'], FsServiceVisitModel::OPEN_STATUSES, true)) {
            return redirect()->to('/operations/field-service/service-visits/' . $id)
                ->with('error', 'Only open visits can be edited.');
        }

        return view('Modules\Operations\Views\field_service\service_visits\edit', array_merge($this->baseViewData(), [
            'title'      => 'Edit Service Visit',
            'visit'      => $visit,
            'employees'  => $this->activeEmployees(),
            'companies'  => $this->companies->getActive(),
            'workOrders' => $this->workOrders->getAssignable(),
        ]));
    }

    public function updateVisit(int $id)
    {
        if (! $this->validate([
            'title'                  => 'required|min_length[2]|max_length[200]',
            'priority'               => 'permit_empty|in_list[low,normal,high,urgent]',
            'status'                 => 'permit_empty|in_list[draft,scheduled,en_route,on_site]',
            'company_id'             => 'permit_empty|is_natural_no_zero',
            'work_order_id'          => 'permit_empty|is_natural_no_zero',
            'technician_employee_id' => 'permit_empty|is_natural_no_zero',
            'scheduled_date'         => 'permit_empty|valid_date',
            'location'               => 'permit_empty|max_length[200]',
        ])) {
            return redirect()->back()->withInput()->with('errors', $this->validator->getErrors());
        }

        $result = $this->service->updateVisit($id, $this->request->getPost(), (int) $this->session->get('user_id'));
        if (! $result['success']) {
            return redirect()->back()->withInput()->with('errors', $result['errors']);
        }

        return redirect()->to('/operations/field-service/service-visits/' . $id)->with('success', 'Service visit updated.');
    }

    public function completeVisit(int $id)
    {
        $result = $this->service->completeVisit($id, (int) $this->session->get('user_id'));
        if (! $result['success']) {
            return redirect()->back()->with('error', $result['error']);
        }

        return redirect()->to('/operations/field-service/service-visits/' . $id)->with('success', 'Service visit completed.');
    }

    public function cancelVisit(int $id)
    {
        $result = $this->service->cancelVisit($id, (int) $this->session->get('user_id'));
        if (! $result['success']) {
            return redirect()->back()->with('error', $result['error']);
        }

        return redirect()->to('/operations/field-service/service-visits/' . $id)->with('success', 'Service visit cancelled.');
    }

    public function destroyVisit(int $id)
    {
        $result = $this->service->deleteVisit($id, (int) $this->session->get('user_id'));
        if (! $result['success']) {
            return redirect()->back()->with('error', $result['error']);
        }

        return redirect()->to('/operations/field-service/service-visits')->with('success', 'Service visit deleted.');
    }

    public function enRouteVisit(int $id)
    {
        $result = $this->service->markVisitEnRoute($id, (int) $this->session->get('user_id'));
        if (! $result['success']) {
            return redirect()->back()->with('error', $result['error']);
        }

        return redirect()->to('/operations/field-service/service-visits/' . $id)->with('success', 'Visit marked en route.');
    }

    public function onSiteVisit(int $id)
    {
        $result = $this->service->markVisitOnSite($id, (int) $this->session->get('user_id'));
        if (! $result['success']) {
            return redirect()->back()->with('error', $result['error']);
        }

        return redirect()->to('/operations/field-service/service-visits/' . $id)->with('success', 'Visit marked on site.');
    }

    // -------------------------------------------------------------------- Jobs

    public function indexJobs()
    {
        $search   = trim((string) $this->request->getGet('q'));
        $status   = $this->request->getGet('status') ?: null;
        $priority = $this->request->getGet('priority') ?: null;

        $entries = $this->jobs
            ->scopeForList($search ?: null, $status, $priority)
            ->orderBy('fs_technician_jobs.due_date', 'ASC')
            ->orderBy('fs_technician_jobs.id', 'DESC')
            ->paginate(25);

        return view('Modules\Operations\Views\field_service\technician_jobs\index', array_merge($this->baseViewData(), [
            'title'   => 'Technician Jobs',
            'entries' => $entries,
            'pager'   => $this->jobs->pager,
            'filters' => compact('search', 'status', 'priority'),
        ]));
    }

    public function createJobForm()
    {
        return view('Modules\Operations\Views\field_service\technician_jobs\create', array_merge($this->baseViewData(), [
            'title'     => 'Create Technician Job',
            'job'       => [],
            'employees' => $this->activeEmployees(),
            'visits'    => $this->visits->getOpenForSelect(),
        ]));
    }

    public function storeJob()
    {
        if (! $this->validate([
            'title'            => 'required|min_length[2]|max_length[200]',
            'employee_id'      => 'required|is_natural_no_zero',
            'priority'         => 'permit_empty|in_list[low,normal,high,urgent]',
            'status'           => 'permit_empty|in_list[assigned,accepted]',
            'service_visit_id' => 'permit_empty|is_natural_no_zero',
            'due_date'         => 'permit_empty|valid_date',
            'role'             => 'permit_empty|max_length[100]',
        ])) {
            return redirect()->back()->withInput()->with('errors', $this->validator->getErrors());
        }

        $result = $this->service->createJob($this->request->getPost(), (int) $this->session->get('user_id'));
        if (! $result['success']) {
            return redirect()->back()->withInput()->with('errors', $result['errors']);
        }

        return redirect()->to('/operations/field-service/technician-jobs/' . $result['id'])
            ->with('success', 'Technician job created.');
    }

    public function showJob(int $id)
    {
        $job = $this->jobs->scopeForList()->where('fs_technician_jobs.id', $id)->first();
        if (! $job) {
            return redirect()->to('/operations/field-service/technician-jobs')->with('error', 'Technician job not found.');
        }

        return view('Modules\Operations\Views\field_service\technician_jobs\show', array_merge($this->baseViewData(), [
            'title' => $job['number'],
            'job'   => $job,
        ]));
    }

    public function editJobForm(int $id)
    {
        $job = $this->jobs->find($id);
        if (! $job) {
            return redirect()->to('/operations/field-service/technician-jobs')->with('error', 'Technician job not found.');
        }

        if (! in_array($job['status'], FsTechnicianJobModel::OPEN_STATUSES, true)) {
            return redirect()->to('/operations/field-service/technician-jobs/' . $id)
                ->with('error', 'Only open jobs can be edited.');
        }

        return view('Modules\Operations\Views\field_service\technician_jobs\edit', array_merge($this->baseViewData(), [
            'title'     => 'Edit Technician Job',
            'job'       => $job,
            'employees' => $this->activeEmployees(),
            'visits'    => $this->visits->getOpenForSelect(),
        ]));
    }

    public function updateJob(int $id)
    {
        if (! $this->validate([
            'title'            => 'required|min_length[2]|max_length[200]',
            'employee_id'      => 'required|is_natural_no_zero',
            'priority'         => 'permit_empty|in_list[low,normal,high,urgent]',
            'status'           => 'permit_empty|in_list[assigned,accepted,in_progress]',
            'service_visit_id' => 'permit_empty|is_natural_no_zero',
            'due_date'         => 'permit_empty|valid_date',
            'role'             => 'permit_empty|max_length[100]',
        ])) {
            return redirect()->back()->withInput()->with('errors', $this->validator->getErrors());
        }

        $result = $this->service->updateJob($id, $this->request->getPost(), (int) $this->session->get('user_id'));
        if (! $result['success']) {
            return redirect()->back()->withInput()->with('errors', $result['errors']);
        }

        return redirect()->to('/operations/field-service/technician-jobs/' . $id)->with('success', 'Technician job updated.');
    }

    public function startJob(int $id)
    {
        $result = $this->service->startJob($id, (int) $this->session->get('user_id'));
        if (! $result['success']) {
            return redirect()->back()->with('error', $result['error']);
        }

        return redirect()->to('/operations/field-service/technician-jobs/' . $id)->with('success', 'Job started.');
    }

    public function completeJob(int $id)
    {
        $result = $this->service->completeJob($id, (int) $this->session->get('user_id'));
        if (! $result['success']) {
            return redirect()->back()->with('error', $result['error']);
        }

        return redirect()->to('/operations/field-service/technician-jobs/' . $id)->with('success', 'Job completed.');
    }

    public function cancelJob(int $id)
    {
        $result = $this->service->cancelJob($id, (int) $this->session->get('user_id'));
        if (! $result['success']) {
            return redirect()->back()->with('error', $result['error']);
        }

        return redirect()->to('/operations/field-service/technician-jobs/' . $id)->with('success', 'Job cancelled.');
    }

    public function destroyJob(int $id)
    {
        $result = $this->service->deleteJob($id, (int) $this->session->get('user_id'));
        if (! $result['success']) {
            return redirect()->back()->with('error', $result['error']);
        }

        return redirect()->to('/operations/field-service/technician-jobs')->with('success', 'Technician job deleted.');
    }

    // ---------------------------------------------------------------- Reports

    public function indexReports()
    {
        $search = trim((string) $this->request->getGet('q'));
        $status = $this->request->getGet('status') ?: null;
        $result = $this->request->getGet('result') ?: null;

        $entries = $this->reports
            ->scopeForList($search ?: null, $status, $result)
            ->orderBy('fs_inspection_reports.id', 'DESC')
            ->paginate(25);

        return view('Modules\Operations\Views\field_service\inspection_reports\index', array_merge($this->baseViewData(), [
            'title'   => 'Inspection Reports',
            'entries' => $entries,
            'pager'   => $this->reports->pager,
            'filters' => ['search' => $search, 'status' => $status, 'result' => $result],
        ]));
    }

    public function createReportForm()
    {
        return view('Modules\Operations\Views\field_service\inspection_reports\create', array_merge($this->baseViewData(), [
            'title'      => 'Create Inspection Report',
            'report'     => [],
            'employees'  => $this->activeEmployees(),
            'visits'     => $this->visits->getOpenForSelect(),
            'workOrders' => $this->workOrders->getAssignable(),
        ]));
    }

    public function storeReport()
    {
        if (! $this->validate([
            'title'                 => 'required|min_length[2]|max_length[200]',
            'result'                => 'permit_empty|in_list[pass,fail,conditional,pending]',
            'inspector_employee_id' => 'permit_empty|is_natural_no_zero',
            'service_visit_id'      => 'permit_empty|is_natural_no_zero',
            'work_order_id'         => 'permit_empty|is_natural_no_zero',
        ])) {
            return redirect()->back()->withInput()->with('errors', $this->validator->getErrors());
        }

        $result = $this->service->createReport($this->request->getPost(), (int) $this->session->get('user_id'));
        if (! $result['success']) {
            return redirect()->back()->withInput()->with('errors', $result['errors']);
        }

        return redirect()->to('/operations/field-service/inspection-reports/' . $result['id'])
            ->with('success', 'Inspection report created.');
    }

    public function showReport(int $id)
    {
        $report = $this->reports->scopeForList()->where('fs_inspection_reports.id', $id)->first();
        if (! $report) {
            return redirect()->to('/operations/field-service/inspection-reports')->with('error', 'Inspection report not found.');
        }

        return view('Modules\Operations\Views\field_service\inspection_reports\show', array_merge($this->baseViewData(), [
            'title'  => $report['number'],
            'report' => $report,
        ]));
    }

    public function editReportForm(int $id)
    {
        $report = $this->reports->find($id);
        if (! $report) {
            return redirect()->to('/operations/field-service/inspection-reports')->with('error', 'Inspection report not found.');
        }

        if (! in_array($report['status'], FsInspectionReportModel::OPEN_STATUSES, true)) {
            return redirect()->to('/operations/field-service/inspection-reports/' . $id)
                ->with('error', 'Only draft or rejected reports can be edited.');
        }

        return view('Modules\Operations\Views\field_service\inspection_reports\edit', array_merge($this->baseViewData(), [
            'title'      => 'Edit Inspection Report',
            'report'     => $report,
            'employees'  => $this->activeEmployees(),
            'visits'     => $this->visits->getOpenForSelect(),
            'workOrders' => $this->workOrders->getAssignable(),
        ]));
    }

    public function updateReport(int $id)
    {
        if (! $this->validate([
            'title'                 => 'required|min_length[2]|max_length[200]',
            'result'                => 'permit_empty|in_list[pass,fail,conditional,pending]',
            'inspector_employee_id' => 'permit_empty|is_natural_no_zero',
            'service_visit_id'      => 'permit_empty|is_natural_no_zero',
            'work_order_id'         => 'permit_empty|is_natural_no_zero',
        ])) {
            return redirect()->back()->withInput()->with('errors', $this->validator->getErrors());
        }

        $result = $this->service->updateReport($id, $this->request->getPost(), (int) $this->session->get('user_id'));
        if (! $result['success']) {
            return redirect()->back()->withInput()->with('errors', $result['errors']);
        }

        return redirect()->to('/operations/field-service/inspection-reports/' . $id)->with('success', 'Inspection report updated.');
    }

    public function submitReport(int $id)
    {
        $result = $this->service->submitReport($id, (int) $this->session->get('user_id'));
        if (! $result['success']) {
            return redirect()->back()->with('error', $result['error']);
        }

        return redirect()->to('/operations/field-service/inspection-reports/' . $id)->with('success', 'Report submitted.');
    }

    public function approveReport(int $id)
    {
        $result = $this->service->approveReport($id, (int) $this->session->get('user_id'));
        if (! $result['success']) {
            return redirect()->back()->with('error', $result['error']);
        }

        return redirect()->to('/operations/field-service/inspection-reports/' . $id)->with('success', 'Report approved.');
    }

    public function rejectReport(int $id)
    {
        $result = $this->service->rejectReport($id, (int) $this->session->get('user_id'));
        if (! $result['success']) {
            return redirect()->back()->with('error', $result['error']);
        }

        return redirect()->to('/operations/field-service/inspection-reports/' . $id)->with('success', 'Report rejected.');
    }

    public function destroyReport(int $id)
    {
        $result = $this->service->deleteReport($id, (int) $this->session->get('user_id'));
        if (! $result['success']) {
            return redirect()->back()->with('error', $result['error']);
        }

        return redirect()->to('/operations/field-service/inspection-reports')->with('success', 'Inspection report deleted.');
    }

    // ------------------------------------------------------------ Signatures

    public function indexSignatures()
    {
        $search = trim((string) $this->request->getGet('q'));
        $status = $this->request->getGet('status') ?: null;

        $entries = $this->signatures
            ->scopeForList($search ?: null, $status)
            ->orderBy('fs_customer_signatures.id', 'DESC')
            ->paginate(25);

        return view('Modules\Operations\Views\field_service\customer_signatures\index', array_merge($this->baseViewData(), [
            'title'   => 'Customer Signatures',
            'entries' => $entries,
            'pager'   => $this->signatures->pager,
            'filters' => compact('search', 'status'),
        ]));
    }

    public function createSignatureForm()
    {
        return view('Modules\Operations\Views\field_service\customer_signatures\create', array_merge($this->baseViewData(), [
            'title'     => 'Capture Signature',
            'signature' => [],
            'companies' => $this->companies->getActive(),
            'visits'    => $this->visits->orderBy('id', 'DESC')->findAll(100),
            'reports'   => $this->reports->orderBy('id', 'DESC')->findAll(100),
        ]));
    }

    public function storeSignature()
    {
        if (! $this->validate([
            'signer_name'          => 'required|min_length[2]|max_length[150]',
            'signer_title'         => 'permit_empty|max_length[100]',
            'company_id'           => 'permit_empty|is_natural_no_zero',
            'service_visit_id'     => 'permit_empty|is_natural_no_zero',
            'inspection_report_id' => 'permit_empty|is_natural_no_zero',
        ])) {
            return redirect()->back()->withInput()->with('errors', $this->validator->getErrors());
        }

        $result = $this->service->createSignature($this->request->getPost(), (int) $this->session->get('user_id'));
        if (! $result['success']) {
            return redirect()->back()->withInput()->with('errors', $result['errors']);
        }

        return redirect()->to('/operations/field-service/customer-signatures/' . $result['id'])
            ->with('success', 'Customer signature captured.');
    }

    public function showSignature(int $id)
    {
        $signature = $this->signatures->scopeForList()->where('fs_customer_signatures.id', $id)->first();
        if (! $signature) {
            return redirect()->to('/operations/field-service/customer-signatures')->with('error', 'Customer signature not found.');
        }

        return view('Modules\Operations\Views\field_service\customer_signatures\show', array_merge($this->baseViewData(), [
            'title'     => $signature['number'],
            'signature' => $signature,
        ]));
    }

    public function editSignatureForm(int $id)
    {
        $signature = $this->signatures->find($id);
        if (! $signature) {
            return redirect()->to('/operations/field-service/customer-signatures')->with('error', 'Customer signature not found.');
        }

        if ($signature['status'] !== 'captured') {
            return redirect()->to('/operations/field-service/customer-signatures/' . $id)
                ->with('error', 'Only captured signatures can be edited.');
        }

        return view('Modules\Operations\Views\field_service\customer_signatures\edit', array_merge($this->baseViewData(), [
            'title'     => 'Edit Signature',
            'signature' => $signature,
            'companies' => $this->companies->getActive(),
            'visits'    => $this->visits->orderBy('id', 'DESC')->findAll(100),
            'reports'   => $this->reports->orderBy('id', 'DESC')->findAll(100),
        ]));
    }

    public function updateSignature(int $id)
    {
        if (! $this->validate([
            'signer_name'          => 'required|min_length[2]|max_length[150]',
            'signer_title'         => 'permit_empty|max_length[100]',
            'company_id'           => 'permit_empty|is_natural_no_zero',
            'service_visit_id'     => 'permit_empty|is_natural_no_zero',
            'inspection_report_id' => 'permit_empty|is_natural_no_zero',
        ])) {
            return redirect()->back()->withInput()->with('errors', $this->validator->getErrors());
        }

        $result = $this->service->updateSignature($id, $this->request->getPost(), (int) $this->session->get('user_id'));
        if (! $result['success']) {
            return redirect()->back()->withInput()->with('errors', $result['errors']);
        }

        return redirect()->to('/operations/field-service/customer-signatures/' . $id)->with('success', 'Signature updated.');
    }

    public function voidSignature(int $id)
    {
        $result = $this->service->voidSignature($id, (int) $this->session->get('user_id'));
        if (! $result['success']) {
            return redirect()->back()->with('error', $result['error']);
        }

        return redirect()->to('/operations/field-service/customer-signatures/' . $id)->with('success', 'Signature voided.');
    }

    public function destroySignature(int $id)
    {
        $result = $this->service->deleteSignature($id, (int) $this->session->get('user_id'));
        if (! $result['success']) {
            return redirect()->back()->with('error', $result['error']);
        }

        return redirect()->to('/operations/field-service/customer-signatures')->with('success', 'Signature deleted.');
    }

    /**
     * @return list<array>
     */
    protected function activeEmployees(): array
    {
        return $this->employees->scopeForList(null, 'active')->orderBy('full_name', 'ASC')->findAll();
    }
}
