<?php
declare(strict_types=1);

require_once __DIR__ . '/../helpers.php';
require_once __DIR__ . '/../corrida_ofertas_helpers.php';
require_once __DIR__ . '/../mercado_pago_helpers.php';
require_once __DIR__ . '/../push.php';

const CANCELAMENTO_TAXA_POR_KM = 1.00;
const CANCELAMENTO_PERCENTUAL_MOTORISTA = 0.85;

$data = input_json();
require_fields($data, ['id_corrida']);

function cancelamento_float(?array $data, string $key): ?float
{
    if (!$data || !isset($data[$key]) || trim((string) $data[$key]) === '') {
        return null;
    }

    return (float) str_replace(',', '.', (string) $data[$key]);
}

function cancelamento_motivo_normalizado(string $motivo): string
{
    $normalizado = strtolower(trim($motivo));
    if (function_exists('iconv')) {
        $ascii = @iconv('UTF-8', 'ASCII//TRANSLIT//IGNORE', $normalizado);
        if ($ascii !== false && $ascii !== '') {
            $normalizado = $ascii;
        }
    }
    $normalizado = preg_replace('/[^a-z0-9]+/', '_', $normalizado) ?? $normalizado;
    return trim($normalizado, '_');
}

function cancelamento_motivo_passageiro_nao_apareceu(string $motivo): bool
{
    $normalizado = cancelamento_motivo_normalizado($motivo);
    return in_array($normalizado, [
        'passageiro_nao_apareceu',
        'passageiro_nao_veio',
        'passageiro_ausente',
        'nao_apareceu',
    ], true);
}

function cancelamento_espera_embarque_expirada(array $corrida): bool
{
    $chegouEm = trim((string) ($corrida['chegou_embarque_em'] ?? ''));
    if ($chegouEm === '') {
        return false;
    }

    $timestamp = strtotime($chegouEm);
    return $timestamp !== false && (time() - $timestamp) >= 180;
}

function cancelamento_calcular(array $corrida, string $canceladaPor, string $motivo): array
{
    $status = (string) ($corrida['status'] ?? '');
    $inicial = cancelamento_float($corrida, 'distancia_motorista_aceite_km')
        ?? cancelamento_float($corrida, 'oferta_distancia_aceite_km');
    $atual = corrida_distancia_km(
        cancelamento_float($corrida, 'motorista_lat'),
        cancelamento_float($corrida, 'motorista_lng'),
        cancelamento_float($corrida, 'origem_lat'),
        cancelamento_float($corrida, 'origem_lng')
    );

    $motivoNaoApareceu = cancelamento_motivo_passageiro_nao_apareceu($motivo);
    $esperaExpirada = cancelamento_espera_embarque_expirada($corrida);
    $cancelamentoNoShowMotorista = $canceladaPor === 'motorista'
        && $status === 'aguardando_embarque'
        && $motivoNaoApareceu
        && $esperaExpirada;

    $percorrida = 0.0;
    if ($cancelamentoNoShowMotorista && $inicial !== null && $inicial > 0) {
        $percorrida = $inicial;
    } elseif ($inicial !== null && $inicial > 0 && $atual !== null) {
        $percorrida = max(0.0, $inicial - $atual);
    } elseif (
        $inicial !== null
        && $inicial > 0
        && in_array($status, ['aguardando_embarque', 'em_andamento'], true)
    ) {
        $percorrida = $inicial;
    }

    $percentual = $inicial !== null && $inicial > 0 ? $percorrida / $inicial : 0.0;
    $deveCobrar = false;
    if ($canceladaPor === 'passageiro') {
        $deveCobrar = in_array($status, ['aguardando_embarque', 'em_andamento'], true)
            || $percentual > 0.5;
    } elseif ($cancelamentoNoShowMotorista) {
        $deveCobrar = true;
    }
    $taxa = $deveCobrar ? round($percorrida * CANCELAMENTO_TAXA_POR_KM, 2) : 0.0;

    return [
        'taxa' => $taxa,
        'distancia_inicial_km' => $inicial === null ? null : round($inicial, 3),
        'distancia_atual_km' => $atual === null ? null : round($atual, 3),
        'distancia_percorrida_km' => round($percorrida, 3),
        'percentual_percorrido' => round($percentual * 100, 2),
        'deve_cobrar' => $deveCobrar,
        'motivo_passageiro_nao_apareceu' => $motivoNaoApareceu,
        'espera_embarque_expirada' => $esperaExpirada,
    ];
}

try {
    $pdo = db();
    $idCorrida = (int) $data['id_corrida'];
    $idPassageiro = (int) ($data['id_passageiro'] ?? $data['id_usuario'] ?? 0);
    $idMotorista = (int) ($data['id_motorista'] ?? 0);
    $motivo = trim((string) ($data['motivo'] ?? ''));
    $observacao = trim((string) ($data['observacao'] ?? ''));
    $canceladaPor = (string) ($data['cancelada_por'] ?? 'passageiro');
    if (!in_array($canceladaPor, ['passageiro', 'motorista', 'admin', 'sistema'], true)) {
        $canceladaPor = 'passageiro';
    }

    $pdo->beginTransaction();

    $latMotorista = cancelamento_float($data, 'lat') ?? cancelamento_float($data, 'latitude');
    $lngMotorista = cancelamento_float($data, 'lng') ?? cancelamento_float($data, 'longitude');
    if ($canceladaPor === 'motorista' && $idMotorista > 0 && $latMotorista !== null && $lngMotorista !== null) {
        motorista_atualizar_localizacao($idMotorista, [
            'lat' => $latMotorista,
            'lng' => $lngMotorista,
            'rumo' => $data['rumo'] ?? $data['heading'] ?? null,
        ]);
    }

    $distanciaAceiteSelect = corrida_schema_column_exists($pdo, 'corridas', 'distancia_motorista_aceite_km')
        ? 'c.distancia_motorista_aceite_km'
        : 'NULL AS distancia_motorista_aceite_km';
    $cancelamentoTaxaExiste = corrida_schema_column_exists($pdo, 'corridas', 'cancelamento_taxa');

    $stmt = $pdo->prepare(
        'SELECT c.*, ' . $distanciaAceiteSelect . ',
                u.ultimo_lat AS motorista_lat,
                u.ultimo_lng AS motorista_lng,
                co.distancia_ate_origem_km AS oferta_distancia_aceite_km
         FROM corridas c
         LEFT JOIN usuarios u ON u.id = c.id_motorista
         LEFT JOIN corrida_ofertas co
           ON co.id_corrida = c.id
          AND co.id_motorista = c.id_motorista
          AND co.status = "aceita"
         WHERE c.id = ?
         ORDER BY co.respondido_em DESC
         LIMIT 1
         FOR UPDATE'
    );
    $stmt->execute([$idCorrida]);
    $corrida = $stmt->fetch();
    if (!$corrida) {
        $pdo->rollBack();
        json_fail('Corrida nao encontrada.', 404);
    }

    if ($idPassageiro > 0 && (int) ($corrida['id_passageiro'] ?? 0) !== $idPassageiro) {
        $pdo->rollBack();
        json_fail('Essa corrida nao pertence a este passageiro.', 403);
    }

    if ($canceladaPor === 'motorista') {
        if ($idMotorista <= 0 || (int) ($corrida['id_motorista'] ?? 0) !== $idMotorista) {
            $pdo->rollBack();
            json_fail('Essa corrida nao pertence a este motorista.', 403);
        }
    }

    $status = (string) ($corrida['status'] ?? '');
    if (!in_array($status, ['pendente', 'aceita', 'aguardando_embarque', 'em_andamento'], true)) {
        $pdo->rollBack();
        json_fail('Corrida nao pode ser cancelada neste estado.', 409);
    }

    $calculo = cancelamento_calcular($corrida, $canceladaPor, $motivo);

    if ($canceladaPor === 'motorista' && $status === 'em_andamento') {
        $pdo->rollBack();
        json_fail('Motorista nao pode cancelar uma viagem em andamento.', 409);
    }

    $redistribuirMotorista = $canceladaPor === 'motorista'
        && in_array($status, ['aceita', 'aguardando_embarque'], true)
        && empty($calculo['deve_cobrar']);

    if ($redistribuirMotorista) {
        if (corrida_table_exists($pdo, 'corrida_ofertas')) {
            $oferta = $pdo->prepare(
                'UPDATE corrida_ofertas
                 SET status = "cancelada",
                     respondido_em = NOW()
                 WHERE id_corrida = ?
                   AND id_motorista = ?
                   AND status IN ("aceita", "pendente")'
            );
            $oferta->execute([$idCorrida, $idMotorista]);
        }

        $setsRedistribuir = [
            'status = "pendente"',
            'id_motorista = NULL',
            'aceita_em = NULL',
        ];
        if (corrida_schema_column_exists($pdo, 'corridas', 'distancia_motorista_aceite_km')) {
            $setsRedistribuir[] = 'distancia_motorista_aceite_km = NULL';
        }
        if (corrida_schema_column_exists($pdo, 'corridas', 'tempo_motorista_aceite_min')) {
            $setsRedistribuir[] = 'tempo_motorista_aceite_min = NULL';
        }
        if (corrida_schema_column_exists($pdo, 'corridas', 'chegou_embarque_em')) {
            $setsRedistribuir[] = 'chegou_embarque_em = NULL';
        }
        if (corrida_schema_column_exists($pdo, 'corridas', 'iniciou_viagem_em')) {
            $setsRedistribuir[] = 'iniciou_viagem_em = NULL';
        }
        if (corrida_schema_column_exists($pdo, 'corridas', 'oferta_lista_liberada')) {
            $setsRedistribuir[] = 'oferta_lista_liberada = 0';
            $setsRedistribuir[] = 'oferta_lista_em = NULL';
        }

        $redistribuir = $pdo->prepare(
            'UPDATE corridas
             SET ' . implode(', ', $setsRedistribuir) . '
             WHERE id = ?
               AND status IN ("aceita", "aguardando_embarque")
               AND id_motorista = ?'
        );
        $redistribuir->execute([$idCorrida, $idMotorista]);

        if ($redistribuir->rowCount() === 0) {
            $pdo->rollBack();
            json_fail('Corrida ja foi alterada.', 409);
        }

        corrida_evento_registrar($pdo, $idCorrida, 'motorista_cancelou_redistribuida', $idMotorista, 'motorista', $status, 'pendente', $motivo !== '' ? $motivo : null, $observacao !== '' ? $observacao : null, [
            'cancelamento' => $calculo,
        ]);
        motorista_metricas_corridas_recalcular($pdo, $idMotorista);

        $novaOferta = corrida_ofertas_despachar($pdo, $idCorrida);
        $pdo->commit();
        json_ok([
            'redistribuida' => true,
            'taxa_cancelamento' => 0,
            'valor_motorista' => 0,
            'cancelamento' => $calculo,
            'nova_oferta_id' => $novaOferta['id'] ?? null,
        ]);
    }

    $taxa = (float) $calculo['taxa'];
    $valorMotorista = $taxa > 0 && !empty($corrida['id_motorista'])
        ? round($taxa * CANCELAMENTO_PERCENTUAL_MOTORISTA, 2)
        : 0.0;
    $pagamentoPixOnline = null;
    $pixOnlinePago = false;
    $pixEstorno = null;
    $formaPagamento = strtolower(trim((string) ($corrida['forma_pagamento'] ?? '')));
    $formaPagamentoDetalhe = strtolower(trim((string) ($corrida['forma_pagamento_detalhe'] ?? '')));
    if ($formaPagamento === 'online' && in_array($formaPagamentoDetalhe, ['pix_online', 'cartao_credito'], true) && mp_table_exists($pdo, 'pagamentos_corridas')) {
        $pagamentoStmt = $pdo->prepare(
            'SELECT *
             FROM pagamentos_corridas
             WHERE id_corrida = ?
               AND metodo IN ("pix_online", "cartao_credito")
               AND status = "aprovado"
             ORDER BY id DESC
             LIMIT 1'
        );
        $pagamentoStmt->execute([$idCorrida]);
        $pagamentoPixOnline = $pagamentoStmt->fetch() ?: null;
        $pixOnlinePago = $pagamentoPixOnline !== null;
    }

    $sets = ['status = "cancelada"'];
    $params = [];
    if (corrida_schema_column_exists($pdo, 'corridas', 'cancelada_em')) {
        $sets[] = 'cancelada_em = NOW()';
    }
    if (corrida_schema_column_exists($pdo, 'corridas', 'cancelada_por')) {
        $sets[] = 'cancelada_por = ?';
        $params[] = $canceladaPor;
    }
    if (corrida_schema_column_exists($pdo, 'corridas', 'cancelamento_motivo')) {
        $sets[] = 'cancelamento_motivo = ?';
        $params[] = $motivo !== '' ? $motivo : null;
    }
    if (corrida_schema_column_exists($pdo, 'corridas', 'cancelamento_observacao')) {
        $sets[] = 'cancelamento_observacao = ?';
        $params[] = $observacao !== '' ? $observacao : null;
    }
    if ($cancelamentoTaxaExiste) {
        $sets[] = 'cancelamento_taxa = ?';
        $params[] = $taxa;
    }
    if (corrida_schema_column_exists($pdo, 'corridas', 'cancelamento_valor_motorista')) {
        $sets[] = 'cancelamento_valor_motorista = ?';
        $params[] = $valorMotorista;
    }
    if (corrida_schema_column_exists($pdo, 'corridas', 'distancia_motorista_cancelamento_km')) {
        $sets[] = 'distancia_motorista_cancelamento_km = ?';
        $params[] = $calculo['distancia_atual_km'];
    }
    $params[] = $idCorrida;

    $up = $pdo->prepare(
        'UPDATE corridas
         SET ' . implode(', ', $sets) . '
         WHERE id = ?
           AND status IN ("pendente", "aceita", "aguardando_embarque", "em_andamento")'
    );
    $up->execute($params);

    if ($up->rowCount() === 0) {
        $pdo->rollBack();
        json_fail('Corrida ja foi alterada.', 409);
    }

    if (corrida_table_exists($pdo, 'corrida_ofertas')) {
        $ofertas = $pdo->prepare(
            'UPDATE corrida_ofertas
             SET status = "cancelada", respondido_em = COALESCE(respondido_em, NOW())
             WHERE id_corrida = ?
               AND status = "pendente"'
        );
        $ofertas->execute([$idCorrida]);
    }

    if ($pixOnlinePago && $pagamentoPixOnline) {
        $valorPagoPix = round((float) ($pagamentoPixOnline['valor_total'] ?? $corrida['valor'] ?? 0), 2);
        $valorEstorno = round(max(0.0, $valorPagoPix - $taxa), 2);
        if ($valorEstorno > 0) {
            try {
                $estornoTotal = $valorEstorno >= $valorPagoPix;
                $refundResponse = mp_reembolsar_pagamento(
                    $pdo,
                    (string) ($pagamentoPixOnline['mp_payment_id'] ?? ''),
                    $estornoTotal ? null : $valorEstorno,
                    'bulo-corrida-refund-' . $idCorrida . '-' . number_format($valorEstorno, 2, '.', '')
                );
                $rawPagamento = json_decode((string) ($pagamentoPixOnline['raw_response'] ?? ''), true);
                if (!is_array($rawPagamento)) {
                    $rawPagamento = [];
                }
                $rawPagamento['_bulo_estorno'] = [
                    'tipo' => $estornoTotal ? 'total' : 'parcial',
                    'valor' => $valorEstorno,
                    'motivo' => 'cancelamento_corrida',
                    'response' => $refundResponse,
                    'registrado_em' => date('Y-m-d H:i:s'),
                ];

                $statusPagamento = $estornoTotal ? 'estornado' : 'aprovado';
                $valorPagamentoRetido = $estornoTotal ? 0.0 : $taxa;
                $valorPlataformaRetido = round(max(0.0, $valorPagamentoRetido - $valorMotorista), 2);
                $pagamentoUp = $pdo->prepare(
                    'UPDATE pagamentos_corridas
                     SET id_motorista = ?,
                         status = ?,
                         valor_total = ?,
                         valor_motorista = ?,
                         valor_plataforma = ?,
                         application_fee = ?,
                         raw_response = ?,
                         atualizado_em = CURRENT_TIMESTAMP
                     WHERE id = ?'
                );
                $pagamentoUp->execute([
                    !empty($corrida['id_motorista']) ? (int) $corrida['id_motorista'] : null,
                    $statusPagamento,
                    $valorPagamentoRetido,
                    $estornoTotal ? 0.0 : $valorMotorista,
                    $estornoTotal ? 0.0 : $valorPlataformaRetido,
                    $estornoTotal ? 0.0 : $valorPlataformaRetido,
                    json_encode($rawPagamento, JSON_UNESCAPED_UNICODE),
                    (int) $pagamentoPixOnline['id'],
                ]);

                if ($estornoTotal && corrida_schema_column_exists($pdo, 'corridas', 'status_pagamento')) {
                    $statusPagamentoUp = $pdo->prepare('UPDATE corridas SET status_pagamento = "estornado" WHERE id = ?');
                    $statusPagamentoUp->execute([$idCorrida]);
                }

                $pixEstorno = [
                    'status' => 'ok',
                    'tipo' => $estornoTotal ? 'total' : 'parcial',
                    'valor' => $valorEstorno,
                ];
            } catch (Throwable $refundError) {
                $pixEstorno = [
                    'status' => 'erro',
                    'valor' => $valorEstorno,
                    'mensagem' => $refundError->getMessage(),
                ];
            }
        } else {
            $pixEstorno = [
                'status' => 'nao_aplicavel',
                'valor' => 0,
            ];
        }
    }

    if ($taxa > 0 && !$pixOnlinePago && corrida_table_exists($pdo, 'passageiro_carteira_movimentos')) {
        $carteira = $pdo->prepare(
            'INSERT INTO passageiro_carteira_movimentos
              (id_passageiro, id_corrida, tipo, origem, valor, descricao, status)
             VALUES (?, ?, "debito", "corrida", ?, ?, "confirmado")'
        );
        $carteira->execute([
            (int) $corrida['id_passageiro'],
            $idCorrida,
            $taxa,
            'Taxa de cancelamento da corrida #' . $idCorrida,
        ]);
    }

    if ($valorMotorista > 0 && corrida_table_exists($pdo, 'ganhos_diarios')) {
        $ganho = $pdo->prepare(
            'INSERT INTO ganhos_diarios (id_motorista, data_ref, total_corridas, total_valor)
             VALUES (?, CURDATE(), 0, ?)
             ON DUPLICATE KEY UPDATE
               total_valor = total_valor + VALUES(total_valor),
               atualizado_em = CURRENT_TIMESTAMP'
        );
        $ganho->execute([(int) $corrida['id_motorista'], $valorMotorista]);
    }

    corrida_evento_registrar(
        $pdo,
        $idCorrida,
        $canceladaPor === 'motorista' ? 'motorista_cancelou_definitiva' : 'corrida_cancelada',
        $canceladaPor === 'motorista' ? (int) ($corrida['id_motorista'] ?? 0) : ($canceladaPor === 'passageiro' ? (int) ($corrida['id_passageiro'] ?? 0) : null),
        $canceladaPor,
        $status,
        'cancelada',
        $motivo !== '' ? $motivo : null,
        $observacao !== '' ? $observacao : null,
        [
            'taxa_cancelamento' => $taxa,
            'valor_motorista' => $valorMotorista,
            'pix_estorno' => $pixEstorno,
            'cancelamento' => $calculo,
        ]
    );
    if ($canceladaPor === 'motorista' && !empty($corrida['id_motorista'])) {
        motorista_metricas_corridas_recalcular($pdo, (int) $corrida['id_motorista']);
    }

    $notificarMotoristaCancelamento = $canceladaPor === 'passageiro' && !empty($corrida['id_motorista']);
    $pdo->commit();

    $pushCancelamentoMotorista = null;
    if ($notificarMotoristaCancelamento && function_exists('push_enviar_cancelamento_corrida_motorista')) {
        try {
            $pushCancelamentoMotorista = push_enviar_cancelamento_corrida_motorista(
                (int) $corrida['id_motorista'],
                $idCorrida
            );
        } catch (Throwable $pushErro) {
            $pushCancelamentoMotorista = [
                'enviados' => 0,
                'falhas' => 1,
                'mensagem' => $pushErro->getMessage(),
            ];
        }
    }

    json_ok([
        'taxa_cancelamento' => $taxa,
        'valor_motorista' => $valorMotorista,
        'saldo_negativo_gerado' => ($taxa > 0 && !$pixOnlinePago) ? -$taxa : 0,
        'pix_estorno' => $pixEstorno,
        'pagamento_estorno' => $pixEstorno,
        'cancelamento' => $calculo,
        'push_cancelamento_motorista' => $pushCancelamentoMotorista,
    ]);
} catch (Throwable $e) {
    if (isset($pdo) && $pdo->inTransaction()) {
        $pdo->rollBack();
    }
    json_fail('Erro ao cancelar corrida.', 500, ['error' => $e->getMessage()]);
}
