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

error_reporting(E_ALL);
ini_set('display_errors', 0);

require dirname(__DIR__) . '/config.php';

function responder($ok, $mensagem, $extra, $status)
{
    http_response_code($status);

    $retorno = array(
        'ok' => $ok,
        'mensagem' => $mensagem
    );

    if (!empty($extra)) {
        $retorno = array_merge($retorno, $extra);
    }

    echo json_encode($retorno);
    exit;
}

function buscarEndereco($latitude, $longitude)
{
    $url =
        'https://nominatim.openstreetmap.org/reverse' .
        '?format=jsonv2' .
        '&lat=' . urlencode($latitude) .
        '&lon=' . urlencode($longitude) .
        '&zoom=18' .
        '&addressdetails=1';

    $json = false;

    /* Tenta primeiro com cURL */
    if (function_exists('curl_init')) {

        $ch = curl_init();

        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 5);
        curl_setopt($ch, CURLOPT_TIMEOUT, 8);
        curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
        curl_setopt(
            $ch,
            CURLOPT_USERAGENT,
            'DetetivesProfissionais-Localizacao/1.0'
        );

        $json = curl_exec($ch);

        curl_close($ch);
    }

    /* Fallback usando stream */
    if ($json === false || $json === '') {

        $contexto = stream_context_create(
            array(
                'http' => array(
                    'method' => 'GET',
                    'timeout' => 8,
                    'header' =>
                        "User-Agent: DetetivesProfissionais-Localizacao/1.0\r\n" .
                        "Accept: application/json\r\n"
                )
            )
        );

        $json = @file_get_contents($url, false, $contexto);
    }

    if ($json === false || $json === '') {
        return null;
    }

    $dados = json_decode($json, true);

    if (!is_array($dados)) {
        return null;
    }

    /*
     * display_name normalmente retorna endereço completo.
     */
    if (
        isset($dados['display_name']) &&
        trim($dados['display_name']) !== ''
    ) {
        return trim($dados['display_name']);
    }

    return null;
}

if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
    responder(
        false,
        'Use uma requisicao POST para enviar a localizacao.',
        array(),
        405
    );
}

/*
 * Aceita:
 * - application/json
 * - application/x-www-form-urlencoded
 * - multipart/form-data
 */

$dados = $_POST;

$raw = file_get_contents('php://input');

if (empty($dados) && !empty($raw)) {

    $json = json_decode($raw, true);

    if (is_array($json)) {
        $dados = $json;
    }
}

$token = isset($dados['token']) ? trim($dados['token']) : '';
$latitude = isset($dados['latitude']) ? trim($dados['latitude']) : '';
$longitude = isset($dados['longitude']) ? trim($dados['longitude']) : '';
$precisao = isset($dados['precisao']) ? trim($dados['precisao']) : null;
$bateria = isset($dados['bateria']) ? trim($dados['bateria']) : null;
$velocidade = isset($dados['velocidade']) ? trim($dados['velocidade']) : null;
$registrado_em = isset($dados['registrado_em']) ? trim($dados['registrado_em']) : '';

if ($token === '') {
    responder(
        false,
        'Token do dispositivo nao informado.',
        array(),
        400
    );
}

if ($latitude === '' || $longitude === '') {
    responder(
        false,
        'Latitude e longitude sao obrigatorias.',
        array(),
        400
    );
}

if (!is_numeric($latitude) || !is_numeric($longitude)) {
    responder(
        false,
        'Latitude ou longitude invalida.',
        array(),
        400
    );
}

$latitude = (float)$latitude;
$longitude = (float)$longitude;

if ($latitude < -90 || $latitude > 90) {
    responder(
        false,
        'Latitude fora do intervalo permitido.',
        array(),
        400
    );
}

if ($longitude < -180 || $longitude > 180) {
    responder(
        false,
        'Longitude fora do intervalo permitido.',
        array(),
        400
    );
}

if (
    $precisao !== null &&
    $precisao !== '' &&
    !is_numeric($precisao)
) {
    responder(
        false,
        'Precisao invalida.',
        array(),
        400
    );
}

if (
    $bateria !== null &&
    $bateria !== '' &&
    !is_numeric($bateria)
) {
    responder(
        false,
        'Nivel de bateria invalido.',
        array(),
        400
    );
}

if (
    $velocidade !== null &&
    $velocidade !== '' &&
    !is_numeric($velocidade)
) {
    responder(
        false,
        'Velocidade invalida.',
        array(),
        400
    );
}

if ($bateria !== null && $bateria !== '') {

    $bateria = (int)$bateria;

    if ($bateria < 0) {
        $bateria = 0;
    }

    if ($bateria > 100) {
        $bateria = 100;
    }

} else {

    $bateria = null;
}

if ($precisao !== null && $precisao !== '') {
    $precisao = (float)$precisao;
} else {
    $precisao = null;
}

if ($velocidade !== null && $velocidade !== '') {
    $velocidade = (float)$velocidade;
} else {
    $velocidade = null;
}

try {

    $stmt = $pdo->prepare(
        'SELECT id, nome, compartilhamento_ativo
         FROM dispositivos
         WHERE token = ?
         LIMIT 1'
    );

    $stmt->execute(array($token));

    $dispositivo = $stmt->fetch();

    if (!$dispositivo) {
        responder(
            false,
            'Dispositivo nao encontrado.',
            array(),
            401
        );
    }

    if ((int)$dispositivo['compartilhamento_ativo'] !== 1) {

        responder(
            false,
            'O compartilhamento de localizacao esta pausado para este dispositivo.',
            array(
                'dispositivo_id' => (int)$dispositivo['id']
            ),
            403
        );
    }

    if ($registrado_em === '') {

        $registrado_em = date('Y-m-d H:i:s');

    } else {

        $timestamp = strtotime($registrado_em);

        if ($timestamp === false) {
            $registrado_em = date('Y-m-d H:i:s');
        } else {
            $registrado_em = date('Y-m-d H:i:s', $timestamp);
        }
    }

    /*
     * Converte latitude/longitude em endereço.
     * Se não conseguir, salva a posição mesmo assim.
     */
    $endereco = buscarEndereco($latitude, $longitude);

    $stmt = $pdo->prepare(
        'INSERT INTO localizacoes
        (
            dispositivo_id,
            latitude,
            longitude,
            endereco,
            precisao,
            bateria,
            velocidade,
            registrado_em
        )
        VALUES
        (?, ?, ?, ?, ?, ?, ?, ?)'
    );

    $stmt->execute(
        array(
            (int)$dispositivo['id'],
            $latitude,
            $longitude,
            $endereco,
            $precisao,
            $bateria,
            $velocidade,
            $registrado_em
        )
    );

    $localizacaoId = (int)$pdo->lastInsertId();

    /*
     * Atualiza horário do dispositivo se a coluna existir.
     */
    try {

        $u = $pdo->prepare(
            'UPDATE dispositivos
             SET atualizado_em = NOW()
             WHERE id = ?'
        );

        $u->execute(
            array(
                (int)$dispositivo['id']
            )
        );

    } catch (Exception $ignorar) {
        /* Coluna atualizado_em pode não existir */
    }

    responder(
        true,
        'Localizacao recebida com sucesso.',
        array(
            'dispositivo_id' => (int)$dispositivo['id'],
            'dispositivo' => $dispositivo['nome'],
            'localizacao_id' => $localizacaoId,
            'endereco' => $endereco,
            'registrado_em' => $registrado_em
        ),
        200
    );

} catch (Exception $e) {

    responder(
        false,
        'Erro interno ao registrar a localizacao.',
        array(),
        500
    );
}
