<?php
declare(strict_types=1);

namespace App\Controllers;

use App\Core\Auth;
use App\Core\Request;
use App\Core\Validator;
use App\Models\Account;
use App\Models\AuditLog;
use App\Models\Contact;
use App\Models\Lead;
use App\Models\Opportunity;
use App\Models\Task;
use App\Models\User;
use App\Services\DocumentStore;
use App\Services\RecordService;

final class TaskController extends BaseController
{
    public function index(Request $request): void
    {
        $this->requireModule('tasks');
        $status = (string) ($_GET['status'] ?? 'open');
        if (!in_array($status, ['open', 'completed', 'cancelled'], true)) {
            $status = 'open';
        }
        $related = (string) ($_GET['related'] ?? 'all');
        if (!in_array($related, Task::relatedTabs(), true)) {
            $related = 'all';
        }
        $when = (string) ($_GET['when'] ?? 'all');
        if (!in_array($when, Task::whenTabs(), true)) {
            $when = 'all';
        }
        $page = $this->page();
        $scope = $this->ownerScope();
        $assigned = (int) ($_GET['assigned'] ?? 0);
        $ownerFilter = $scope;
        if ($scope === null && $assigned > 0) {
            $ownerFilter = $assigned;
        }
        $owners = User::salespeople(Auth::instance()->tenantId());
        if ($scope === null) {
            $seen = array_map('intval', array_column($owners, 'id'));
            foreach (User::servicePeople((int) Auth::instance()->tenantId()) as $sp) {
                $sid = (int) $sp['id'];
                if (!in_array($sid, $seen, true)) {
                    $owners[] = $sp;
                    $seen[] = $sid;
                }
            }
        }
        $result = Task::paginate($this->q(), $status, $page, 20, $ownerFilter, $related, $when);
        view('tasks/index', [
            'title' => 'Tasks',
            'rows' => $result['rows'],
            'total' => $result['total'],
            'page' => $page,
            'pages' => max(1, (int) ceil($result['total'] / 20)),
            'q' => $this->q(),
            'status' => $status,
            'related' => $related,
            'when' => $when,
            'assigned' => $assigned,
            'showAssigned' => $scope === null,
            'owners' => $owners,
            'counts' => Task::boardCounts($ownerFilter, $related),
            'notifications' => $this->notifications(),
        ]);
    }

    public function store(Request $request): void
    {
        $this->requireModule('tasks');
        $this->verifyCsrf($request);
        $data = $request->all();
        $v = (new Validator())->required($data, ['title' => 'Task title']);
        if ($v->fails()) {
            $this->fail($v);
        }
        $relatedType = trim((string) ($data['related_type'] ?? ''));
        $relatedId = (int) ($data['related_id'] ?? 0);
        $allowed = ['lead', 'opportunity', 'account', 'contact'];
        if ($relatedType !== '' && !in_array($relatedType, $allowed, true)) {
            json_out(['ok' => false, 'error' => 'A task can be on a lead, deal, or account.'], 422);
        }
        if ($relatedType !== '' && $relatedId < 1) {
            json_out(['ok' => false, 'error' => 'Pick which lead, deal, or account this task is for.'], 422);
        }
        if ($relatedType === '' || $relatedId < 1) {
            $data['related_type'] = '';
            $data['related_id'] = 0;
        } else {
            $found = match ($relatedType) {
                'lead' => Lead::find($relatedId),
                'opportunity' => Opportunity::find($relatedId),
                'account' => Account::find($relatedId),
                'contact' => Contact::find($relatedId),
                default => null,
            };
            if (!$found) {
                json_out(['ok' => false, 'error' => 'That record was not found.'], 404);
            }
        }
        $file = $_FILES['file'] ?? [];
        $fileName = null;
        if (is_array($file) && (int) ($file['error'] ?? UPLOAD_ERR_NO_FILE) !== UPLOAD_ERR_NO_FILE) {
            try {
                $fileName = DocumentStore::acceptAttachment($file);
            } catch (\RuntimeException $e) {
                json_out(['ok' => false, 'error' => $e->getMessage()], 422);
            }
        }
        $id = RecordService::addTask($data);
        if ($fileName) {
            try {
                DocumentStore::saveAttachment('task', $id, (string) $file['tmp_name'], $fileName);
                Task::update($id, ['file_name' => $fileName]);
            } catch (\RuntimeException $e) {
                json_out(['ok' => false, 'error' => $e->getMessage()], 500);
            }
        }
        AuditLog::write('task.create', 'task', $id);
        json_out(['ok' => true, 'id' => $id, 'message' => 'Task created.']);
    }

    public function file(Request $request): void
    {
        $this->requireModule('tasks');
        $id = (int) $request->param('id');
        $task = Task::find($id);
        if (!$task || empty($task['file_name'])) {
            http_response_code(404);
            echo 'Not found.';
            return;
        }
        $scope = $this->ownerScope();
        if ($scope && (int) $task['owner_id'] !== $scope) {
            http_response_code(403);
            echo 'You cannot open this file.';
            return;
        }
        $tid = (int) Auth::instance()->tenantId();
        if (!DocumentStore::existsAttachment($tid, 'task', $id, (string) $task['file_name'])) {
            http_response_code(404);
            echo 'No file on this task.';
            return;
        }
        DocumentStore::streamAttachment($tid, 'task', $id, (string) $task['file_name']);
    }

    public function update(Request $request): void
    {
        $this->requireModule('tasks');
        $this->verifyCsrf($request);
        $id = (int) $request->param('id');
        $this->findOwnedTask($id);
        $status = (string) $request->input('status', '');
        if (!in_array($status, ['open', 'completed', 'cancelled'], true)) {
            json_out(['ok' => false, 'error' => 'Pick Open, Done, or Cancelled.'], 422);
        }
        Task::setStatus($id, $status);
        AuditLog::write('task.update', 'task', $id, ['status' => $status]);
        json_out(['ok' => true, 'status' => $status, 'message' => 'Status updated.']);
    }

    public function complete(Request $request): void
    {
        $this->requireModule('tasks');
        $this->verifyCsrf($request);
        $id = (int) $request->param('id');
        $this->findOwnedTask($id);
        $done = (int) $request->input('completed', 1) === 1;
        Task::setStatus($id, $done ? 'completed' : 'open');
        json_out(['ok' => true]);
    }

    private function findOwnedTask(int $id): array
    {
        $task = Task::find($id);
        if (!$task) {
            json_out(['ok' => false, 'error' => 'Not found.'], 404);
        }
        $scope = $this->ownerScope();
        if ($scope && (int) $task['owner_id'] !== $scope) {
            json_out(['ok' => false, 'error' => 'You cannot update this task.'], 403);
        }
        return $task;
    }
}
