<?php

declare(strict_types=1);

require_once __DIR__ . '/../db.php';
require_once __DIR__ . '/../auth.php';

header('Content-Type: application/json; charset=utf-8');

function jsonResponse(array $data, int $status = 200): void
{
    http_response_code($status);
    header('Cache-Control: no-store, no-cache, must-revalidate, max-age=0');
    header('Pragma: no-cache');
    echo json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
    exit;
}

function getInputData(): array
{
    $contentType = $_SERVER['CONTENT_TYPE'] ?? '';

    if (str_contains($contentType, 'application/json')) {
        $raw = file_get_contents('php://input');
        $data = json_decode($raw, true);

        return is_array($data) ? $data : [];
    }

    return $_POST;
}

function optionResponse(array $option): array
{
    return [
        'id' => (int)$option['id'],
        'name' => $option['name'],
        'icon' => $option['logo'] !== null
            ? 'data:image/svg+xml;base64,' . base64_encode($option['logo'])
            : null
    ];
}

function customFieldResponse(array $field): array
{
    if ($field['default_value'] === null) {
        return [
            'id' => (int)$field['id'],
            'task_type' => (int)$field['task_type'],
            'name' => $field['name'],
            'type' => $field['type'],
            'value' => null,
            'raw_value' => ''
        ];
    }

    $decodedValue = json_decode((string)$field['default_value'], true);
    $decodeOk = json_last_error() === JSON_ERROR_NONE;
    $rawValue = (string)$field['default_value'];

    if ($decodeOk) {
        if ($field['type'] === 'json') {
            $rawValue = json_encode($decodedValue, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
        } elseif ($field['type'] === 'boolean') {
            $rawValue = $decodedValue ? 'true' : 'false';
        } else {
            $rawValue = (string)$decodedValue;
        }
    }

    return [
        'id' => (int)$field['id'],
        'task_type' => (int)$field['task_type'],
        'name' => $field['name'],
        'type' => $field['type'],
        'value' => $decodeOk ? $decodedValue : null,
        'raw_value' => $rawValue
    ];
}

function pictureDataUri(?string $picture): ?string
{
    if ($picture === null || $picture === '') {
        return null;
    }

    $info = @getimagesizefromstring($picture);
    $mime = is_array($info) && isset($info['mime'])
        ? $info['mime']
        : 'image/png';

    return 'data:' . $mime . ';base64,' . base64_encode($picture);
}

function userResponse(array $user): array
{
    return [
        'id' => (int)$user['id'],
        'name' => $user['name'],
        'email' => $user['email'],
        'picture' => pictureDataUri($user['picture'] ?? null)
    ];
}

function rightResponse(array $right): array
{
    return [
        'id' => (int)$right['id'],
        'name' => $right['name']
    ];
}

function projectResponse(array $project): array
{
    return [
        'id' => $project['id'],
        'name' => $project['name'],
        'owner' => (int)$project['owner']
    ];
}

function optionTable(string $kind): string
{
    return match ($kind) {
        'type' => 'task_types',
        'priority' => 'task_priorities',
        default => jsonResponse([
            'success' => false,
            'error' => 'Option kind must be type or priority.'
        ], 400)
    };
}

function optionLabel(string $kind): string
{
    return $kind === 'type' ? 'Task type' : 'Task priority';
}

function normalizeName(mixed $name, string $label, int $maxLength = 128): string
{
    $name = trim((string)$name);

    if ($name === '') {
        jsonResponse(['success' => false, 'error' => "{$label} name is required."], 400);
    }

    if (strlen($name) > $maxLength) {
        jsonResponse(['success' => false, 'error' => "{$label} name is too long."], 400);
    }

    return $name;
}

function normalizeCustomFieldType(mixed $type): string
{
    $type = strtolower(trim((string)$type));
    $allowedTypes = ['string', 'int', 'float', 'date', 'boolean', 'text', 'json'];

    if (!in_array($type, $allowedTypes, true)) {
        jsonResponse([
            'success' => false,
            'error' => 'Custom field type must be string, int, float, date, boolean, text, or json.'
        ], 400);
    }

    return $type;
}

function normalizeCustomFieldValue(mixed $value, string $type): ?string
{
    $rawValue = trim((string)$value);

    if ($rawValue === '') {
        return null;
    }

    $normalized = match ($type) {
        'int' => filter_var($rawValue, FILTER_VALIDATE_INT, FILTER_NULL_ON_FAILURE),
        'float' => is_numeric($rawValue) ? (float)$rawValue : null,
        'boolean' => match (strtolower($rawValue)) {
            '1', 'true', 'yes', 'on' => true,
            '0', 'false', 'no', 'off' => false,
            default => null
        },
        'date' => preg_match('/^\d{4}-\d{2}-\d{2}$/', $rawValue) ? $rawValue : null,
        'json' => json_decode($rawValue, true),
        default => $rawValue
    };

    if ($type === 'json' && json_last_error() !== JSON_ERROR_NONE) {
        jsonResponse(['success' => false, 'error' => 'Custom field JSON value is invalid.'], 400);
    }

    if ($normalized === null && $type !== 'json') {
        jsonResponse(['success' => false, 'error' => "Custom field value does not match {$type}."], 400);
    }

    return json_encode($normalized, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
}

function ensureCustomTaskFieldsTable(): void
{
    db()->exec(
        'CREATE TABLE IF NOT EXISTS custom_task_fields (
            id INT NOT NULL AUTO_INCREMENT,
            task_type INT NOT NULL,
            name VARCHAR(128) NOT NULL,
            type VARCHAR(128) NOT NULL,
            default_value TEXT NULL,
            PRIMARY KEY (id),
            INDEX idx_custom_task_fields_task_type (task_type)
        ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci'
    );

    $columns = db()->query('SHOW COLUMNS FROM custom_task_fields')->fetchAll(PDO::FETCH_COLUMN);

    if (!in_array('default_value', $columns, true)) {
        db()->exec('ALTER TABLE custom_task_fields ADD COLUMN default_value TEXT NULL');
    }

    if (in_array('value', $columns, true)) {
        db()->exec('UPDATE custom_task_fields SET default_value = `value` WHERE default_value IS NULL');
    }
}

function ensureCustomFieldValuesTable(): void
{
    db()->exec(
        'CREATE TABLE IF NOT EXISTS custom_field_values (
            id INT NOT NULL AUTO_INCREMENT,
            field_id INT NOT NULL,
            task_id VARCHAR(26) NOT NULL,
            `value` TEXT NULL,
            PRIMARY KEY (id),
            INDEX idx_custom_field_values_task (task_id),
            INDEX idx_custom_field_values_field (field_id)
        ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci'
    );
}

function uploadedLogo(): ?string
{
    if (!isset($_FILES['logo']) || !is_array($_FILES['logo'])) {
        return null;
    }

    $file = $_FILES['logo'];

    if (($file['error'] ?? UPLOAD_ERR_NO_FILE) === UPLOAD_ERR_NO_FILE) {
        return null;
    }

    if (($file['error'] ?? UPLOAD_ERR_OK) !== UPLOAD_ERR_OK) {
        jsonResponse(['success' => false, 'error' => 'Logo upload failed.'], 400);
    }

    if (($file['size'] ?? 0) > 1024 * 1024) {
        jsonResponse(['success' => false, 'error' => 'Logo must be 1 MB or smaller.'], 400);
    }

    $content = file_get_contents($file['tmp_name']);

    if ($content === false || trim($content) === '') {
        jsonResponse(['success' => false, 'error' => 'Logo file is empty.'], 400);
    }

    if (!preg_match('/<svg\b/i', $content)) {
        jsonResponse(['success' => false, 'error' => 'Logo must be an SVG file.'], 400);
    }

    return $content;
}

function requireOption(string $table, int $id, string $label): void
{
    if ($id <= 0) {
        jsonResponse(['success' => false, 'error' => "{$label} must be valid."], 400);
    }

    $stmt = db()->prepare("SELECT id FROM {$table} WHERE id = ? LIMIT 1");
    $stmt->execute([$id]);

    if (!$stmt->fetch()) {
        jsonResponse(['success' => false, 'error' => "{$label} not found."], 404);
    }
}

function preventDeleteIfUsed(string $kind, int $id): void
{
    if ($kind === 'type') {
        $taskStmt = db()->prepare('SELECT COUNT(*) FROM tasks WHERE type = ?');
        $taskStmt->execute([$id]);
        $assignmentStmt = db()->prepare('SELECT COUNT(*) FROM assigned_task_states WHERE task_type = ?');
        $assignmentStmt->execute([$id]);

        if ((int)$taskStmt->fetchColumn() > 0 || (int)$assignmentStmt->fetchColumn() > 0) {
            jsonResponse([
                'success' => false,
                'error' => 'Task type is still used by tasks or workflow assignments.'
            ], 409);
        }

        return;
    }

    $stmt = db()->prepare('SELECT COUNT(*) FROM tasks WHERE priority = ?');
    $stmt->execute([$id]);

    if ((int)$stmt->fetchColumn() > 0) {
        jsonResponse([
            'success' => false,
            'error' => 'Task priority is still used by tasks.'
        ], 409);
    }
}

function requireCustomField(int $fieldId): array
{
    if ($fieldId <= 0) {
        jsonResponse(['success' => false, 'error' => 'Custom field must be valid.'], 400);
    }

    ensureCustomTaskFieldsTable();

    $stmt = db()->prepare(
        'SELECT id, task_type, name, type, default_value
         FROM custom_task_fields
         WHERE id = ?
         LIMIT 1'
    );
    $stmt->execute([$fieldId]);
    $field = $stmt->fetch();

    if (!$field) {
        jsonResponse(['success' => false, 'error' => 'Custom field not found.'], 404);
    }

    return $field;
}

function requireUserId(int $userId): void
{
    if ($userId <= 0) {
        jsonResponse(['success' => false, 'error' => 'User must be valid.'], 400);
    }

    $stmt = db()->prepare('SELECT id FROM users WHERE id = ? LIMIT 1');
    $stmt->execute([$userId]);

    if (!$stmt->fetch()) {
        jsonResponse(['success' => false, 'error' => 'User not found.'], 404);
    }
}

function requireRightId(int $rightId): void
{
    if ($rightId <= 0) {
        jsonResponse(['success' => false, 'error' => 'Right must be valid.'], 400);
    }

    $stmt = db()->prepare('SELECT id FROM rights WHERE id = ? LIMIT 1');
    $stmt->execute([$rightId]);

    if (!$stmt->fetch()) {
        jsonResponse(['success' => false, 'error' => 'Right not found.'], 404);
    }
}

function requireProjectId(string $projectId): void
{
    if ($projectId === '') {
        jsonResponse(['success' => false, 'error' => 'Project must be valid.'], 400);
    }

    $stmt = db()->prepare('SELECT id FROM projects WHERE id = ? LIMIT 1');
    $stmt->execute([$projectId]);

    if (!$stmt->fetch()) {
        jsonResponse(['success' => false, 'error' => 'Project not found.'], 404);
    }
}

function rightName(int $rightId): ?string
{
    $stmt = db()->prepare('SELECT name FROM rights WHERE id = ? LIMIT 1');
    $stmt->execute([$rightId]);
    $name = $stmt->fetchColumn();

    return $name !== false ? (string)$name : null;
}

function optionsData(): array
{
    $types = array_map('optionResponse', db()->query(
        'SELECT id, name, logo
         FROM task_types
         ORDER BY id ASC'
    )->fetchAll());

    $priorities = array_map('optionResponse', db()->query(
        'SELECT id, name, logo
         FROM task_priorities
         ORDER BY id ASC'
    )->fetchAll());

    try {
        ensureCustomTaskFieldsTable();

        $customFields = array_map('customFieldResponse', db()->query(
            'SELECT id, task_type, name, type, default_value
             FROM custom_task_fields
             ORDER BY task_type ASC, id ASC'
        )->fetchAll());
    } catch (Throwable $e) {
        $customFields = [];
    }

    $users = array_map('userResponse', db()->query(
        'SELECT id, name, email, picture
         FROM users
         ORDER BY name ASC, id ASC'
    )->fetchAll());

    $rights = array_map('rightResponse', db()->query(
        'SELECT id, name
         FROM rights
         ORDER BY id ASC'
    )->fetchAll());

    $userRights = db()->query(
        'SELECT user_id, right_id
         FROM user_rights
         ORDER BY user_id ASC, right_id ASC'
    )->fetchAll();

    $projects = array_map('projectResponse', db()->query(
        'SELECT id, name, owner
         FROM projects
         ORDER BY name ASC, id ASC'
    )->fetchAll());

    $userAccess = db()->query(
        'SELECT id, user_id, project_id
         FROM user_access
         ORDER BY user_id ASC, project_id ASC'
    )->fetchAll();

    return [
        'types' => $types,
        'priorities' => $priorities,
        'custom_fields' => $customFields,
        'users' => $users,
        'rights' => $rights,
        'user_rights' => array_map(static fn (array $row): array => [
            'user_id' => (int)$row['user_id'],
            'right_id' => (int)$row['right_id']
        ], $userRights),
        'projects' => $projects,
        'user_access' => array_map(static fn (array $row): array => [
            'id' => (int)$row['id'],
            'user_id' => (int)$row['user_id'],
            'project_id' => $row['project_id']
        ], $userAccess)
    ];
}

$user = requireApiAuth();
requireUserRight((int)$user['id'], 'Admin');

$api = $_GET['api'] ?? '';

switch ($api) {
    case 'OptionsData': {
        jsonResponse([
            'success' => true,
            'options' => optionsData()
        ]);
    }

    case 'CreateOption': {
        if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
            jsonResponse(['success' => false, 'error' => 'CreateOption requires POST.'], 405);
        }

        $data = getInputData();
        $kind = (string)($data['kind'] ?? '');
        $table = optionTable($kind);
        $label = optionLabel($kind);
        $name = normalizeName($data['name'] ?? '', $label, $kind === 'type' ? 20 : 128);
        $logo = uploadedLogo();

        $stmt = db()->prepare("INSERT INTO {$table} (name, logo) VALUES (?, ?)");
        $stmt->execute([$name, $logo]);

        jsonResponse([
            'success' => true,
            'option_id' => (int)db()->lastInsertId()
        ], 201);
    }

    case 'UpdateOption': {
        if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
            jsonResponse(['success' => false, 'error' => 'UpdateOption requires POST.'], 405);
        }

        $data = getInputData();
        $kind = (string)($data['kind'] ?? '');
        $table = optionTable($kind);
        $label = optionLabel($kind);
        $id = (int)($data['id'] ?? 0);
        $name = normalizeName($data['name'] ?? '', $label, $kind === 'type' ? 20 : 128);
        $logo = uploadedLogo();
        $removeLogo = !empty($data['remove_logo']);

        requireOption($table, $id, $label);

        if ($logo !== null) {
            $stmt = db()->prepare("UPDATE {$table} SET name = ?, logo = ? WHERE id = ?");
            $stmt->execute([$name, $logo, $id]);
        } elseif ($removeLogo) {
            $stmt = db()->prepare("UPDATE {$table} SET name = ?, logo = NULL WHERE id = ?");
            $stmt->execute([$name, $id]);
        } else {
            $stmt = db()->prepare("UPDATE {$table} SET name = ? WHERE id = ?");
            $stmt->execute([$name, $id]);
        }

        jsonResponse([
            'success' => true,
            'option_id' => $id
        ]);
    }

    case 'DeleteOption': {
        if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
            jsonResponse(['success' => false, 'error' => 'DeleteOption requires POST.'], 405);
        }

        $kind = (string)($_GET['kind'] ?? '');
        $table = optionTable($kind);
        $label = optionLabel($kind);
        $id = (int)($_GET['id'] ?? 0);

        requireOption($table, $id, $label);
        preventDeleteIfUsed($kind, $id);

        if ($kind === 'type') {
            ensureCustomTaskFieldsTable();
            ensureCustomFieldValuesTable();
            db()->prepare(
                'DELETE custom_field_values
                 FROM custom_field_values
                 INNER JOIN custom_task_fields ON custom_task_fields.id = custom_field_values.field_id
                 WHERE custom_task_fields.task_type = ?'
            )->execute([$id]);
            db()->prepare('DELETE FROM custom_task_fields WHERE task_type = ?')->execute([$id]);
        }

        $stmt = db()->prepare("DELETE FROM {$table} WHERE id = ?");
        $stmt->execute([$id]);

        jsonResponse([
            'success' => true,
            'option_id' => $id
        ]);
    }

    case 'CreateCustomField': {
        if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
            jsonResponse(['success' => false, 'error' => 'CreateCustomField requires POST.'], 405);
        }

        $data = getInputData();
        $taskType = (int)($data['task_type'] ?? 0);
        $name = normalizeName($data['name'] ?? '', 'Custom field');
        $type = normalizeCustomFieldType($data['type'] ?? '');
        $value = normalizeCustomFieldValue($data['value'] ?? '', $type);

        requireOption('task_types', $taskType, 'Task type');
        ensureCustomTaskFieldsTable();

        $stmt = db()->prepare(
            'INSERT INTO custom_task_fields (task_type, name, type, default_value)
             VALUES (?, ?, ?, ?)'
        );
        $stmt->execute([$taskType, $name, $type, $value]);

        jsonResponse([
            'success' => true,
            'field_id' => (int)db()->lastInsertId()
        ], 201);
    }

    case 'UpdateCustomField': {
        if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
            jsonResponse(['success' => false, 'error' => 'UpdateCustomField requires POST.'], 405);
        }

        $data = getInputData();
        $fieldId = (int)($data['id'] ?? 0);
        $field = requireCustomField($fieldId);
        $name = normalizeName($data['name'] ?? '', 'Custom field');
        $type = normalizeCustomFieldType($data['type'] ?? '');
        $value = normalizeCustomFieldValue($data['value'] ?? '', $type);
        ensureCustomTaskFieldsTable();

        $stmt = db()->prepare(
            'UPDATE custom_task_fields
             SET name = ?, type = ?, default_value = ?
             WHERE id = ?'
        );
        $stmt->execute([$name, $type, $value, $field['id']]);

        jsonResponse([
            'success' => true,
            'field_id' => (int)$field['id']
        ]);
    }

    case 'DeleteCustomField': {
        if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
            jsonResponse(['success' => false, 'error' => 'DeleteCustomField requires POST.'], 405);
        }

        $fieldId = (int)($_GET['field_id'] ?? 0);
        $field = requireCustomField($fieldId);
        ensureCustomTaskFieldsTable();

        $stmt = db()->prepare('DELETE FROM custom_task_fields WHERE id = ?');
        $stmt->execute([$field['id']]);

        jsonResponse([
            'success' => true,
            'field_id' => (int)$field['id']
        ]);
    }

    case 'SetUserRight': {
        if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
            jsonResponse(['success' => false, 'error' => 'SetUserRight requires POST.'], 405);
        }

        $data = getInputData();
        $userId = (int)($data['user_id'] ?? 0);
        $rightId = (int)($data['right_id'] ?? 0);
        $enabled = !empty($data['enabled']);

        requireUserId($userId);
        requireRightId($rightId);

        if (!$enabled && $userId === (int)$user['id'] && rightName($rightId) === 'Admin') {
            jsonResponse(['success' => false, 'error' => 'You cannot remove your own Admin right.'], 400);
        }

        if ($enabled) {
            $stmt = db()->prepare(
                'SELECT id
                 FROM user_rights
                 WHERE user_id = ?
                   AND right_id = ?
                 LIMIT 1'
            );
            $stmt->execute([$userId, $rightId]);

            if (!$stmt->fetch()) {
                db()->prepare(
                    'INSERT INTO user_rights (user_id, right_id)
                     VALUES (?, ?)'
                )->execute([$userId, $rightId]);
            }
        } else {
            db()->prepare(
                'DELETE FROM user_rights
                 WHERE user_id = ?
                   AND right_id = ?'
            )->execute([$userId, $rightId]);
        }

        jsonResponse([
            'success' => true,
            'user_id' => $userId,
            'right_id' => $rightId,
            'enabled' => $enabled
        ]);
    }

    case 'GrantProjectAccess': {
        if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
            jsonResponse(['success' => false, 'error' => 'GrantProjectAccess requires POST.'], 405);
        }

        $data = getInputData();
        $userId = (int)($data['user_id'] ?? 0);
        $projectId = strtoupper(trim((string)($data['project_id'] ?? '')));

        requireUserId($userId);
        requireProjectId($projectId);

        $stmt = db()->prepare(
            'SELECT id
             FROM user_access
             WHERE user_id = ?
               AND project_id = ?
             LIMIT 1'
        );
        $stmt->execute([$userId, $projectId]);

        if (!$stmt->fetch()) {
            db()->prepare(
                'INSERT INTO user_access (user_id, project_id)
                 VALUES (?, ?)'
            )->execute([$userId, $projectId]);
        }

        jsonResponse([
            'success' => true,
            'user_id' => $userId,
            'project_id' => $projectId
        ], 201);
    }

    case 'RevokeProjectAccess': {
        if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
            jsonResponse(['success' => false, 'error' => 'RevokeProjectAccess requires POST.'], 405);
        }

        $accessId = (int)($_GET['access_id'] ?? 0);

        if ($accessId <= 0) {
            jsonResponse(['success' => false, 'error' => 'Project access must be valid.'], 400);
        }

        db()->prepare('DELETE FROM user_access WHERE id = ?')->execute([$accessId]);

        jsonResponse([
            'success' => true,
            'access_id' => $accessId
        ]);
    }

    default:
        jsonResponse([
            'success' => false,
            'error' => 'Unknown admin options API action.'
        ], 400);
}
