<?php
error_reporting(E_ALL);
ini_set('display_errors', 0);

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

ob_start();

require_once __DIR__ . '/../config/koneksi.php';
require_once __DIR__ . '/../config/jwt.php';

function login_generate_request_id()
{
    try {
        return 'login_' . bin2hex(random_bytes(8));
    } catch (Throwable $e) {
        return 'login_' . str_replace('.', '', uniqid('', true));
    }
}

function login_set_request_id($requestId)
{
    $requestId = trim((string)$requestId);
    if ($requestId === '') {
        $requestId = login_generate_request_id();
    }

    $GLOBALS['login_request_id'] = $requestId;
    return $requestId;
}

function login_get_request_id()
{
    if (empty($GLOBALS['login_request_id'])) {
        return login_set_request_id('');
    }

    return (string)$GLOBALS['login_request_id'];
}

function login_resolve_log_file()
{
    $logDir = dirname(__DIR__) . '/logs';
    if (!is_dir($logDir)) {
        @mkdir($logDir, 0775, true);
    }

    return $logDir . '/login.log';
}

function login_mask_value($value, $visibleStart = 3, $visibleEnd = 2)
{
    $value = (string)$value;
    $length = strlen($value);
    if ($length <= ($visibleStart + $visibleEnd)) {
        return str_repeat('*', $length);
    }

    return substr($value, 0, $visibleStart)
        . str_repeat('*', $length - $visibleStart - $visibleEnd)
        . substr($value, -$visibleEnd);
}

function login_log($message, array $context = [], $level = 'INFO')
{
    $line = sprintf(
        "[login][%s][%s] %s %s\n",
        strtoupper($level),
        login_get_request_id(),
        $message,
        json_encode($context, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)
    );

    @file_put_contents(login_resolve_log_file(), $line, FILE_APPEND);
}

function login_debug_enabled()
{
    if (isset($_GET['debug']) && $_GET['debug'] === '1') {
        return true;
    }

    $headers = function_exists('getallheaders') ? getallheaders() : [];
    foreach ($headers as $key => $value) {
        if (strtolower($key) === 'x-debug-login' && trim((string)$value) === '1') {
            return true;
        }
    }

    return false;
}

function login_json_response($success, $message, $data = null, $httpCode = 200)
{
    http_response_code($httpCode);

    $response = [
        'success' => (bool)$success,
        'message' => $message,
    ];

    if ($data !== null) {
        foreach ($data as $key => $value) {
            $response[$key] = $value;
        }
    }

    $response['request_id'] = login_get_request_id();

    login_log(
        $success ? 'response success' : 'response error',
        [
            'http_code' => $httpCode,
            'message' => $message,
            'has_data' => $data !== null,
        ],
        $success ? 'INFO' : 'ERROR'
    );

    echo json_encode($response, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
    exit;
}

set_error_handler(function ($severity, $message, $file, $line) {
    throw new ErrorException($message, 0, $severity, $file, $line);
});

set_exception_handler(function ($e) {
    while (ob_get_level() > 0) {
        ob_end_clean();
    }

    $message = 'Terjadi kesalahan saat memproses login.';
    if (login_debug_enabled()) {
        $message = $e->getMessage() . ' @ ' . $e->getFile() . ':' . $e->getLine();
    }

    login_log('exception thrown', [
        'error_class' => get_class($e),
        'error_message' => $e->getMessage(),
        'error_file' => $e->getFile(),
        'error_line' => $e->getLine(),
        'trace' => $e->getTraceAsString(),
    ], 'ERROR');

    login_json_response(false, $message, null, 500);
});

register_shutdown_function(function () {
    $error = error_get_last();
    if (!$error) {
        return;
    }

    $fatalTypes = [E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR, E_USER_ERROR];
    if (!in_array($error['type'], $fatalTypes, true)) {
        return;
    }

    while (ob_get_level() > 0) {
        ob_end_clean();
    }

    http_response_code(500);
    header('Content-Type: application/json; charset=UTF-8');

    $message = 'Terjadi kesalahan fatal di server login.';
    if (login_debug_enabled()) {
        $message = $error['message'] . ' @ ' . $error['file'] . ':' . $error['line'];
    }

    login_log('fatal shutdown', [
        'type' => $error['type'],
        'message' => $error['message'],
        'file' => $error['file'],
        'line' => $error['line'],
    ], 'ERROR');

    echo json_encode([
        'success' => false,
        'message' => $message,
        'request_id' => login_get_request_id(),
    ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
});

function login_fail($message, $httpCode = 400)
{
    login_json_response(false, $message, null, $httpCode);
}

function login_new_id($prefix)
{
    try {
        return $prefix . '_' . bin2hex(random_bytes(12));
    } catch (Exception $e) {
        return $prefix . '_' . uniqid();
    }
}

function login_pick_connection()
{
    global $conn, $koneksi;

    if (isset($conn) && $conn instanceof mysqli) {
        return $conn;
    }

    if (isset($koneksi) && $koneksi instanceof mysqli) {
        return $koneksi;
    }

    login_fail('Koneksi database tidak tersedia', 500);
}

function login_generate_token(array $payload)
{
    if (function_exists('generateJWT')) {
        return generateJWT($payload);
    }

    if (function_exists('generate_jwt')) {
        return generate_jwt($payload);
    }

    login_fail('Fungsi generate JWT tidak ditemukan', 500);
}

function login_table_row($stmt)
{
    $stmt->execute();
    $metadata = $stmt->result_metadata();
    if (!$metadata) {
        return null;
    }

    $row = [];
    $bindValues = [];
    $fields = $metadata->fetch_fields();

    foreach ($fields as $field) {
        $row[$field->name] = null;
        $bindValues[] = &$row[$field->name];
    }

    call_user_func_array([$stmt, 'bind_result'], $bindValues);

    if (!$stmt->fetch()) {
        return null;
    }

    $result = [];
    foreach ($row as $key => $value) {
        $result[$key] = $value;
    }

    return $result;
}

function login_null_if_blank($value)
{
    $value = trim((string)$value);
    return $value === '' ? null : $value;
}

function login_new_uuid()
{
    $bytes = random_bytes(16);
    $bytes[6] = chr((ord($bytes[6]) & 0x0f) | 0x40);
    $bytes[8] = chr((ord($bytes[8]) & 0x3f) | 0x80);
    $hex = bin2hex($bytes);

    return substr($hex, 0, 8) . '-'
        . substr($hex, 8, 4) . '-'
        . substr($hex, 12, 4) . '-'
        . substr($hex, 16, 4) . '-'
        . substr($hex, 20, 12);
}

function login_table_exists(mysqli $conn, $table)
{
    $safeTable = $conn->real_escape_string($table);
    $result = $conn->query("SHOW TABLES LIKE '{$safeTable}'");
    return $result instanceof mysqli_result && $result->num_rows > 0;
}

function login_column_exists(mysqli $conn, $table, $column)
{
    if (!login_table_exists($conn, $table)) {
        return false;
    }

    $safeTable = str_replace('`', '', $table);
    $safeColumn = $conn->real_escape_string($column);
    $result = $conn->query("SHOW COLUMNS FROM `{$safeTable}` LIKE '{$safeColumn}'");
    return $result instanceof mysqli_result && $result->num_rows > 0;
}

function login_users_table(mysqli $conn)
{
    if (login_table_exists($conn, 'users')) {
        return 'users';
    }

    if (login_table_exists($conn, 'app_users')) {
        return 'app_users';
    }

    throw new Exception('Tabel users/app_users tidak ditemukan');
}

function login_user_company_column(mysqli $conn)
{
    $usersTable = login_users_table($conn);

    if (login_column_exists($conn, $usersTable, 'company_id')) {
        return 'company_id';
    }

    if (login_column_exists($conn, $usersTable, 'store_id')) {
        return 'store_id';
    }

    throw new Exception('Kolom company_id/store_id tidak ditemukan di tabel ' . $usersTable);
}

function login_user_company_id(array $user)
{
    $candidates = [
        $user['company_id'] ?? null,
        $user['store_id'] ?? null,
        $user['companyId'] ?? null,
        $user['storeId'] ?? null,
    ];

    foreach ($candidates as $value) {
        $resolved = login_null_if_blank($value);
        if ($resolved !== null) {
            return $resolved;
        }
    }

    return null;
}

function login_fetch_user_by_email(mysqli $conn, $email)
{
    $usersTable = login_users_table($conn);
    $where = 'email = ?';
    if (login_column_exists($conn, $usersTable, 'is_deleted')) {
        $where .= ' AND is_deleted = 0';
    }

    $stmt = $conn->prepare("SELECT * FROM {$usersTable} WHERE {$where} LIMIT 1");
    if (!$stmt) {
        throw new Exception('Gagal menyiapkan query user');
    }

    $stmt->bind_param('s', $email);
    $user = login_table_row($stmt);
    $stmt->close();

    return $user ?: null;
}

function login_insert_company(mysqli $conn, $companyId, $companyName, $ownerUserId, $planType, $now)
{
    $columns = ['id', 'name', 'plan_type'];
    $values = [$companyId, $companyName, $planType];
    $types = 'sss';
    $trialExpiredAt = date('Y-m-d H:i:s', strtotime((string)$now . ' +7 days'));

    if (login_column_exists($conn, 'companies', 'owner_user_id')) {
        $columns[] = 'owner_user_id';
        $values[] = $ownerUserId;
        $types .= 's';
    }

    if (login_column_exists($conn, 'companies', 'expired_at')) {
        $columns[] = 'expired_at';
        $values[] = $trialExpiredAt;
        $types .= 's';
    }

    if (login_column_exists($conn, 'companies', 'is_active')) {
        $columns[] = 'is_active';
        $values[] = 1;
        $types .= 'i';
    }

    if (login_column_exists($conn, 'companies', 'created_at')) {
        $columns[] = 'created_at';
        $values[] = $now;
        $types .= 's';
    }

    if (login_column_exists($conn, 'companies', 'updated_at')) {
        $columns[] = 'updated_at';
        $values[] = $now;
        $types .= 's';
    }

    $placeholders = implode(', ', array_fill(0, count($columns), '?'));
    $sql = 'INSERT INTO companies (' . implode(', ', $columns) . ') VALUES (' . $placeholders . ')';
    $stmt = $conn->prepare($sql);

    if (!$stmt) {
        throw new Exception('Gagal menyiapkan insert company');
    }

    $refs = [];
    foreach ($values as $index => $value) {
        $refs[$index] = &$values[$index];
    }

    $bindArgs = array_merge([$types], $refs);
    call_user_func_array([$stmt, 'bind_param'], $bindArgs);

    if (!$stmt->execute()) {
        $error = $stmt->error;
        $stmt->close();
        throw new Exception('Gagal membuat company baru: ' . $error);
    }

    $stmt->close();
}

function login_insert_user(mysqli $conn, $userId, $companyId, $name, $email, $passwordToStore, $role, $loginProvider, $now, $userUuid = null)
{
    $usersTable = login_users_table($conn);
    $companyColumn = login_user_company_column($conn);

    $columns = ['id', $companyColumn, 'name', 'email', 'password'];
    $values = [$userId, $companyId, $name, $email, $passwordToStore];
    $types = 'sssss';

    if (login_column_exists($conn, $usersTable, 'uuid')) {
        $columns[] = 'uuid';
        $values[] = login_null_if_blank($userUuid) ?? login_new_uuid();
        $types .= 's';
    }

    if (login_column_exists($conn, $usersTable, 'role')) {
        $columns[] = 'role';
        $values[] = $role;
        $types .= 's';
    }

    if (login_column_exists($conn, $usersTable, 'login_provider')) {
        $columns[] = 'login_provider';
        $values[] = $loginProvider;
        $types .= 's';
    }

    if (login_column_exists($conn, $usersTable, 'is_active')) {
        $columns[] = 'is_active';
        $values[] = 1;
        $types .= 'i';
    }

    if (login_column_exists($conn, $usersTable, 'created_at')) {
        $columns[] = 'created_at';
        $values[] = $now;
        $types .= 's';
    }

    if (login_column_exists($conn, $usersTable, 'updated_at')) {
        $columns[] = 'updated_at';
        $values[] = $now;
        $types .= 's';
    }

    if (login_column_exists($conn, $usersTable, 'last_login_at')) {
        $columns[] = 'last_login_at';
        $values[] = $now;
        $types .= 's';
    }

    $placeholders = implode(', ', array_fill(0, count($columns), '?'));
    $sql = 'INSERT INTO ' . $usersTable . ' (' . implode(', ', $columns) . ') VALUES (' . $placeholders . ')';
    $stmt = $conn->prepare($sql);

    if (!$stmt) {
        throw new Exception('Gagal menyiapkan insert user');
    }

    $refs = [];
    foreach ($values as $index => $value) {
        $refs[$index] = &$values[$index];
    }

    $bindArgs = array_merge([$types], $refs);
    call_user_func_array([$stmt, 'bind_param'], $bindArgs);

    if (!$stmt->execute()) {
        $error = $stmt->error;
        $stmt->close();
        throw new Exception('Gagal membuat user baru: ' . $error);
    }

    $stmt->close();
}

function login_update_last_login_at(mysqli $conn, $userId, $now)
{
    $usersTable = login_users_table($conn);

    if (!login_column_exists($conn, $usersTable, 'last_login_at')) {
        return;
    }

    $stmt = $conn->prepare('UPDATE ' . $usersTable . ' SET last_login_at = ? WHERE id = ?');
    if (!$stmt) {
        throw new Exception('Gagal update login user');
    }

    $stmt->bind_param('ss', $now, $userId);
    $stmt->execute();
    $stmt->close();
}

function login_ensure_user_uuid(mysqli $conn, array &$user)
{
    $usersTable = login_users_table($conn);

    if (!login_column_exists($conn, $usersTable, 'uuid')) {
        $user['uuid'] = (string)($user['id'] ?? '');
        return;
    }

    $uuid = trim((string)($user['uuid'] ?? ''));
    if ($uuid !== '') {
        $user['uuid'] = $uuid;
        return;
    }

    $uuid = login_new_uuid();
    $stmt = $conn->prepare('UPDATE ' . $usersTable . ' SET uuid = ? WHERE id = ?');
    if (!$stmt) {
        throw new Exception('Gagal menyiapkan update uuid user');
    }

    $userId = (string)($user['id'] ?? '');
    $stmt->bind_param('ss', $uuid, $userId);
    if (!$stmt->execute()) {
        $error = $stmt->error;
        $stmt->close();
        throw new Exception('Gagal menyimpan uuid user: ' . $error);
    }
    $stmt->close();

    $user['uuid'] = $uuid;
}

$conn = login_pick_connection();
login_set_request_id($_SERVER['HTTP_X_REQUEST_ID'] ?? '');
$input = json_decode(file_get_contents('php://input'), true);

login_log('login request received', [
    'method' => $_SERVER['REQUEST_METHOD'] ?? '',
    'uri' => $_SERVER['REQUEST_URI'] ?? '',
    'remote_addr' => $_SERVER['REMOTE_ADDR'] ?? '',
    'content_type' => $_SERVER['CONTENT_TYPE'] ?? '',
    'email' => is_array($input) ? login_mask_value((string)($input['email'] ?? '')) : '',
    'has_password' => is_array($input) ? array_key_exists('password', $input) : false,
    'has_name' => is_array($input) ? array_key_exists('name', $input) : false,
]);

if (!is_array($input)) {
    login_fail('Request tidak valid');
}

$email = strtolower(trim((string)($input['email'] ?? '')));
$password = array_key_exists('password', $input) ? (string)$input['password'] : null;
$name = trim((string)($input['name'] ?? 'User'));
$fcmToken = login_null_if_blank($input['fcm_token'] ?? null);
$deviceName = trim((string)($input['device_name'] ?? 'unknown'));
$now = date('Y-m-d H:i:s');

if ($email === '') {
    login_fail('Email wajib diisi');
}

if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
    login_fail('Format email tidak valid');
}

if ($name === '') {
    $name = 'User';
}

$user = login_fetch_user_by_email($conn, $email);
login_log('user lookup finished', [
    'email' => login_mask_value($email),
    'user_found' => $user !== null,
]);

$isEmailPasswordLogin = $password !== null && trim($password) !== '';

if ($isEmailPasswordLogin) {
    if (!$user) {
        login_fail('User tidak ditemukan', 404);
    }

    $storedPassword = (string)($user['password'] ?? '');
    $validPassword = false;

    if ($storedPassword !== '') {
        $validPassword =
            hash_equals($storedPassword, sha1($password)) ||
            password_verify($password, $storedPassword);
    }

    if (!$validPassword) {
        login_fail('Password salah', 401);
    }
}

if (!$user) {
    $userId = login_new_id('usr');
    $companyId = login_new_id('cmp');
    $companyName = 'Toko ' . preg_replace('/[^a-z0-9._-]/i', '', explode('@', $email)[0]);
    if ($companyName === 'Toko ') {
        $companyName = 'Toko Saya';
    }

    $loginProvider = $isEmailPasswordLogin ? 'email' : 'google';
    $passwordToStore = '';

    $conn->begin_transaction();

    try {
        $planType = 'free';
        $role = 'owner';
        $userUuid = login_new_uuid();
        login_log('creating new user and company', [
            'user_id' => $userId,
            'company_id' => $companyId,
            'user_uuid' => $userUuid,
            'login_provider' => $loginProvider,
        ]);
        login_insert_company($conn, $companyId, $companyName, $userUuid, $planType, $now);
        login_insert_user($conn, $userId, $companyId, $name, $email, $passwordToStore, $role, $loginProvider, $now, $userUuid);

        $conn->commit();
    } catch (Exception $e) {
        $conn->rollback();
        login_fail($e->getMessage(), 500);
    }

    $user = login_fetch_user_by_email($conn, $email);
    login_log('reload inserted user finished', [
        'email' => login_mask_value($email),
        'user_found' => $user !== null,
    ]);
}

if (!$user) {
    login_fail('User gagal dimuat', 500);
}

login_ensure_user_uuid($conn, $user);

if (isset($user['is_active']) && (int)$user['is_active'] !== 1) {
    login_fail('Akun tidak aktif', 403);
}

login_update_last_login_at($conn, $user['id'], $now);

$resolvedCompanyId = login_user_company_id($user);
login_log('resolved company from user', [
    'user_id' => (string)($user['id'] ?? ''),
    'user_uuid' => (string)($user['uuid'] ?? ''),
    'company_id' => $resolvedCompanyId,
    'has_company_id' => isset($user['company_id']),
    'has_store_id' => isset($user['store_id']),
]);

if ($resolvedCompanyId === null) {
    login_fail('company_id user tidak ditemukan', 500);
}

$stmt = $conn->prepare('SELECT * FROM companies WHERE id = ? LIMIT 1');
if (!$stmt) {
    login_fail('Gagal mengambil company', 500);
}
$stmt->bind_param('s', $resolvedCompanyId);
$company = login_table_row($stmt);
$stmt->close();

if (!$company) {
    login_fail('Company tidak ditemukan', 404);
}

$company['owner_user_id'] = (string)($user['uuid'] ?? $user['id']);

$planType = strtolower(trim((string)($company['plan_type'] ?? 'free')));
if ($planType === 'trial') {
    $planType = 'free';
}
if (!in_array($planType, ['free', 'basic', 'pro', 'business'], true)) {
    $planType = 'free';
}

$tokenPayload = [
    'id' => (string)$user['id'],
    'user_id' => (string)$user['id'],
    'uuid' => (string)($user['uuid'] ?? $user['id']),
    'user_uuid' => (string)($user['uuid'] ?? $user['id']),
    'email' => (string)$user['email'],
    'name' => (string)($user['name'] ?? $name),
    'role' => (string)($user['role'] ?? 'owner'),
    'company_id' => (string)$resolvedCompanyId,
    'login_provider' => (string)($user['login_provider'] ?? ''),
    'device_name' => $deviceName,
    'fcm_token' => $fcmToken,
];

$token = login_generate_token($tokenPayload);

login_json_response(true, 'Login berhasil', [
    'data' => [
        'id' => (string)$user['id'],
        'uuid' => (string)($user['uuid'] ?? $user['id']),
        'email' => (string)$user['email'],
        'name' => (string)($user['name'] ?? $name),
        'photo' => isset($user['photo']) ? $user['photo'] : null,
        'company_id' => (string)$resolvedCompanyId,
        'role' => (string)($user['role'] ?? 'owner'),
        'company' => [
            'id' => (string)$company['id'],
            'name' => (string)($company['name'] ?? 'Toko Saya'),
            'expired_at' => $company['expired_at'] ?? null,
            'plan_type' => $planType,
        ],
    ],
    'token' => $token,
]);
