<?php
declare(strict_types=1);

namespace App\Controllers;

use App\Core\Auth;
use App\Core\Request;
use App\Core\Validator;
use App\Industry\Catalog;
use App\Models\Account;
use App\Models\Activity;
use App\Models\AuditLog;
use App\Models\Task;
use App\Models\User;
use App\Services\IndustryService;
use App\Services\RecordService;

final class ServiceController extends BaseController
{
    public function index(Request $request): void
    {
        $this->requireModule('service_visits');
        $scope = has_role('manager') ? null : Auth::instance()->id();
        $assigned = (int) ($_GET['assigned'] ?? 0);
        $assigneeFilter = $scope;
        if ($scope === null && $assigned > 0) {
            $assigneeFilter = $assigned;
        }
        $when = (string) ($_GET['when'] ?? 'all');
        if (!in_array($when, ['all', 'today', 'late', 'soon', 'attended'], true)) {
            $when = 'all';
        }
        $week = (string) ($_GET['week'] ?? 'all');
        if (!in_array($week, ['all', 'this'], true)) {
            $week = 'all';
        }
        $ready = Catalog::isServiceSetup();
        $kpiRows = $ready ? Account::visits($assigneeFilter) : [];
        $rows = $ready ? Account::visits($assigneeFilter, [
            'q' => $this->q(),
            'when' => $when,
            'week' => $week,
        ]) : [];
        view('service/index', [
            'title' => 'Service visits',
            'isServiceSetup' => $ready,
            'rows' => $rows,
            'kpiRows' => $kpiRows,
            'q' => $this->q(),
            'when' => $when,
            'week' => $week,
            'assigned' => $assigned,
            'showAssigned' => $scope === null,
            'assignees' => User::servicePeople(Auth::instance()->tenantId()),
            'accounts' => Account::allForSelect(),
            'notifications' => $this->notifications(),
        ]);
    }

    public function show(Request $request): void
    {
        $this->requireModule('service_visits');
        $id = (int) $request->param('id');
        $visit = Account::visitDetail($id);
        if (!$visit) {
            flash('error', 'Visit not found.');
            redirect('service');
        }
        $uid = (int) Auth::instance()->id();
        if (!has_role('manager', 'admin')
            && (int) ($visit['service_assignee_id'] ?? 0) !== $uid
            && (int) ($visit['owner_id'] ?? 0) !== $uid) {
            http_response_code(403);
            view('errors/403', ['title' => 'No access']);
            return;
        }
        $timeline = Activity::timeline('account', $id);
        $visitFeed = [];
        foreach ($timeline as $row) {
            if (($row['type'] ?? '') === 'visit') {
                $visitFeed[] = $row;
            }
        }
        view('service/show', [
            'title' => (string) ($visit['visit_subject'] ?: ($visit['name'] ?? 'Visit')),
            'visit' => $visit,
            'visitFeed' => $visitFeed,
            'tasks' => Task::forRelated('account', $id),
            'notifications' => $this->notifications(),
        ]);
    }

    public function note(Request $request): void
    {
        $this->requireModule('service_visits');
        $this->verifyCsrf($request);
        if (!Catalog::isServiceSetup()) {
            json_out(['ok' => false, 'error' => 'Visit fields are available after Admin picks the Service setup.'], 422);
        }
        $data = $request->all();
        $v = (new Validator())->required($data, ['account_id' => 'Account', 'body' => 'Notes']);
        if ($v->fails()) {
            $this->fail($v);
        }
        $account = Account::find((int) $data['account_id']);
        if (!$account) {
            json_out(['ok' => false, 'error' => 'Account not found.'], 404);
        }
        if (!has_role('manager', 'admin') && (int) $account['service_assignee_id'] !== (int) Auth::instance()->id()
            && (int) $account['owner_id'] !== (int) Auth::instance()->id()) {
            json_out(['ok' => false, 'error' => 'This visit is not assigned to you.'], 403);
        }
        RecordService::logActivity('visit', 'Visit notes', (string) $data['body'], 'account', (int) $account['id']);
        AuditLog::write('service.visit_note', 'account', (int) $account['id']);
        json_out(['ok' => true, 'message' => 'Visit notes saved.', 'redirect' => url('service/' . (int) $account['id'])]);
    }

    public function attend(Request $request): void
    {
        $this->requireModule('service_visits');
        $this->verifyCsrf($request);
        if (!Catalog::isServiceSetup()) {
            json_out(['ok' => false, 'error' => 'Visit fields are available after Admin picks the Service setup.'], 422);
        }
        $id = (int) $request->param('id');
        $account = Account::find($id);
        if (!$account) {
            json_out(['ok' => false, 'error' => 'Visit not found.'], 404);
        }
        if (!has_role('manager', 'admin') && (int) $account['service_assignee_id'] !== (int) Auth::instance()->id()
            && (int) $account['owner_id'] !== (int) Auth::instance()->id()) {
            json_out(['ok' => false, 'error' => 'This visit is not assigned to you.'], 403);
        }
        $notes = trim((string) $request->input('body', ''));
        Account::update($id, [
            'last_visit_at' => date('Y-m-d H:i:s'),
            'next_visit_at' => null,
        ]);
        RecordService::logActivity(
            'visit',
            'Visit attended',
            $notes !== '' ? $notes : 'Marked as attended.',
            'account',
            $id
        );
        AuditLog::write('service.visit_attend', 'account', $id);
        json_out([
            'ok' => true,
            'message' => 'Marked attended.',
        ]);
    }

    public function request(Request $request): void
    {
        $this->requireModule('service_visits');
        $this->verifyCsrf($request);
        if (!Catalog::isServiceSetup()) {
            json_out(['ok' => false, 'error' => 'Visit fields are available after Admin picks the Service setup.'], 422);
        }
        $data = $request->all();
        $v = (new Validator())->required($data, [
            'account_id' => 'Account',
            'subject' => 'Subject',
            'visit_at' => 'Visit date',
            'body' => 'Details',
        ]);
        if ($v->fails()) {
            $this->fail($v);
        }
        $account = Account::find((int) $data['account_id']);
        if (!$account) {
            json_out(['ok' => false, 'error' => 'Account not found.'], 404);
        }
        $when = trim((string) $data['visit_at']);
        $ts = strtotime(str_replace('T', ' ', $when));
        if ($ts === false) {
            json_out(['ok' => false, 'error' => 'Visit date is not valid.'], 422);
        }
        $assigneeId = (int) Auth::instance()->id();
        if (has_role('manager', 'admin')) {
            if (!empty($data['service_assignee_id'])) {
                $assigneeId = (int) $data['service_assignee_id'];
            } elseif (!empty($account['service_assignee_id'])) {
                $assigneeId = (int) $account['service_assignee_id'];
            }
        }
        $subject = trim((string) ($data['subject'] ?? ''));
        $extra = IndustryService::sanitizeAttrs($data['attrs'] ?? [], entity_fields('service'));
        $body = trim((string) $data['body']);
        foreach ($extra as $k => $val) {
            $body .= "\n" . $k . ': ' . (is_scalar($val) ? (string) $val : '');
        }
        Account::update((int) $account['id'], [
            'next_visit_at' => date('Y-m-d H:i:s', $ts),
            'service_assignee_id' => $assigneeId,
            'visit_subject' => $subject,
            'visit_detail' => $body !== '' ? $body : null,
        ]);
        RecordService::logActivity(
            'visit',
            $subject,
            $body !== '' ? $body : null,
            'account',
            (int) $account['id']
        );
        AuditLog::write('service.request', 'account', (int) $account['id']);
        json_out([
            'ok' => true,
            'message' => 'Visit added to the list.',
            'redirect' => url('service'),
        ]);
    }
}
