<?php
namespace Modules\Fornecedores\Controllers;

use Gestoreasy\Core\Http\Request;
use Gestoreasy\Core\Http\Response;
use Gestoreasy\Core\View\View;
use Gestoreasy\Core\Tenant\ConnectionManager;
use Gestoreasy\Core\Validation\Br;
use Gestoreasy\Core\Audit\AuditLogger;

class SuppliersCrudController {
    // Helpers
    public function geocode(Request $req): Response {
        $q = $req->get('q');
        if ($q) {
            $url = 'https://nominatim.openstreetmap.org/search?format=json&limit=1&q=' . urlencode($q);
            $ctx = stream_context_create(['http' => [
                'method' => 'GET',
                'timeout' => 8,
                'header' => "Accept: application/json\r\nUser-Agent: gestoreasy/fornecedores-module"
            ]]);
            $res = @file_get_contents($url, false, $ctx);
            if ($res === false) { return new Response(200, ['ok' => false, 'error' => 'request_failed']); }
            $json = json_decode($res, true);
            if (!is_array($json) || count($json) === 0) { return new Response(200, ['ok' => false, 'error' => 'not_found']); }
            $it = $json[0];
            return new Response(200, ['ok' => true, 'lat' => floatval($it['lat']), 'lon' => floatval($it['lon'])]);
        }
        $addr = [
            'logradouro' => $req->get('logradouro') ?? '',
            'numero' => $req->get('numero') ?? '',
            'bairro' => $req->get('bairro') ?? '',
            'cidade' => $req->get('cidade') ?? '',
            'estado' => $req->get('estado') ?? '',
            'cep' => $req->get('cep') ?? '',
        ];
        $q = trim(implode(' ', array_filter($addr)));
        if ($q === '') return new Response(422, ['error' => ['code' => 'invalid_address']]);
        $url = 'https://nominatim.openstreetmap.org/search?format=json&limit=1&q=' . urlencode($q);
        $ctx = stream_context_create(['http' => [
            'method' => 'GET',
            'timeout' => 8,
            'header' => "Accept: application/json\r\nUser-Agent: gestoreasy/fornecedores-module"
        ]]);
        $res = @file_get_contents($url, false, $ctx);
        if ($res === false) { return new Response(200, ['ok' => false, 'error' => 'request_failed']); }
        $json = json_decode($res, true);
        if (!is_array($json) || count($json) === 0) { return new Response(200, ['ok' => false, 'error' => 'not_found']); }
        $it = $json[0];
        return new Response(200, ['ok' => true, 'lat' => floatval($it['lat']), 'lon' => floatval($it['lon'])]);
    }

    public function cepLookup(Request $r): Response {
        $params = $r->get('params', []);
        $cep = preg_replace('/\D/', '', (string)($params[0] ?? ''));
        if (strlen($cep) !== 8) return new Response(422, ['error' => ['code' => 'invalid_cep', 'message' => 'CEP inválido']]);
        $url = 'https://viacep.com.br/ws/' . $cep . '/json/';
        $ctx = stream_context_create(['http' => ['method' => 'GET','timeout' => 6,'header' => "Accept: application/json"]]);
        $res = @file_get_contents($url, false, $ctx);
        if ($res === false) return new Response(502, ['error' => ['code' => 'cep_service_unavailable']]);
        $data = json_decode($res, true);
        if (!is_array($data) || isset($data['erro'])) return new Response(404, ['error' => ['code' => 'cep_not_found']]);
        $norm = [
            'logradouro' => $data['logradouro'] ?? null,
            'complemento' => $data['complemento'] ?? null,
            'bairro' => $data['bairro'] ?? null,
            'cidade' => $data['localidade'] ?? null,
            'estado' => $data['uf'] ?? null,
        ];
        return Response::json(['data' => $norm]);
    }

    public function cnpjLookup(Request $r): Response {
        $params = $r->get('params', []);
        $cnpj = preg_replace('/\D/', '', (string)($params[0] ?? ''));
        if (!Br::isCNPJ($cnpj)) return new Response(422, ['error' => ['code' => 'invalid_cnpj', 'message' => 'CNPJ inválido']]);
        // BrasilAPI primeiro
        $data = null;
        try {
            $ctx = stream_context_create(['http' => ['method' => 'GET','timeout' => 8,'header' => "Accept: application/json"]]);
            $res = @file_get_contents('https://brasilapi.com.br/api/cnpj/v1/' . $cnpj, false, $ctx);
            if ($res !== false) { $data = json_decode($res, true); }
        } catch (\Throwable $e) {}
        if (!$data) {
            try {
                $ctx = stream_context_create(['http' => ['method' => 'GET','timeout' => 10,'header' => "Accept: application/json"]]);
                $res = @file_get_contents('https://www.receitaws.com.br/v1/cnpj/' . $cnpj, false, $ctx);
                if ($res !== false) { $data = json_decode($res, true); }
            } catch (\Throwable $e) {}
        }
        if (!$data) return new Response(502, ['error' => ['code' => 'cnpj_service_unavailable', 'message' => 'Falha ao consultar serviço de CNPJ']]);
        $norm = [
            'cnpj' => $cnpj,
            'razao_social' => $data['razao_social'] ?? ($data['nome'] ?? null),
            'nome_fantasia' => $data['nome_fantasia'] ?? ($data['fantasia'] ?? null),
            'telefone' => $data['telefone'] ?? null,
            'email' => $data['email'] ?? null,
            'situacao' => $data['descricao_situacao_cadastral'] ?? ($data['situacao'] ?? null),
            'data_situacao' => $data['data_situacao_cadastral'] ?? ($data['data_situacao'] ?? null),
            'atividade_principal' => [
                'codigo' => (string)($data['cnae_fiscal'] ?? ($data['atividade_principal'][0]['code'] ?? '')),
                'descricao' => $data['cnae_fiscal_descricao'] ?? ($data['atividade_principal'][0]['text'] ?? null),
            ],
            'endereco' => [
                'logradouro' => $data['logradouro'] ?? null,
                'numero' => $data['numero'] ?? null,
                'bairro' => $data['bairro'] ?? null,
                'cidade' => $data['municipio'] ?? null,
                'estado' => $data['uf'] ?? null,
                'cep' => $data['cep'] ?? null,
            ]
        ];
        return Response::json(['data' => $norm]);
    }

    // CRUD
    public function list(Request $r): Response {
        $pdo = ConnectionManager::forTenant();
        $page = max(1, (int)($r->query['page'] ?? 1));
        $per = min(100, max(10, (int)($r->query['per_page'] ?? 20)));
        $q = trim((string)($r->query['q'] ?? ''));
        $where = '';
        $params = [];
        if ($q !== '') {
            $where = 'WHERE nome LIKE ? OR cpf LIKE ? OR cnpj LIKE ?';
            $like = "%$q%";
            $params = [$like, $like, $like];
        }
        $offset = ($page - 1) * $per;
        $sql = "SELECT id, nome, nome_fantasia, razao_social, tipo, cpf, cnpj, telefone_fixo, telefone_celular, email, contato_nome, site_url, referencias, limite_credito, bloqueado, inativo, status_motivo FROM fornecedores $where ORDER BY id DESC LIMIT $per OFFSET $offset";
        $stmt = $pdo->prepare($sql);
        $stmt->execute($params);
        $rows = $stmt->fetchAll();
        $countStmt = $pdo->prepare("SELECT COUNT(*) AS c FROM fornecedores $where");
        $countStmt->execute($params);
        $total = (int)$countStmt->fetch()['c'];
        $totalPages = (int)ceil($total / $per);
        AuditLogger::logRead('fornecedores', null, ['endpoint' => 'fornecedores.list', 'q' => $q, 'page' => $page, 'per_page' => $per]);
        return Response::json(['data' => $rows, 'page' => $page, 'per_page' => $per, 'total' => $total, 'total_pages' => $totalPages]);
    }

    public function getById(Request $r): Response {
        $pdo = ConnectionManager::forTenant();
        $id = (int)($r->get('params')[0] ?? 0);
        if ($id <= 0) return new Response(422, ['error' => ['code' => 'invalid_id', 'message' => 'ID inválido']]);
        $stmt = $pdo->prepare('SELECT id, nome, nome_fantasia, razao_social, tipo, cpf, cnpj, ie, im, telefone_fixo, telefone_celular, referencias, limite_credito, imagem_url, arquivo_url, email, contato_nome, site_url, observacoes, bloqueado, inativo, status_motivo, created_at, updated_at FROM fornecedores WHERE id = ?');
        $stmt->execute([$id]);
        $row = $stmt->fetch();
        if (!$row) return new Response(404, ['error' => ['code' => 'not_found', 'message' => 'Fornecedor não encontrado']]);
        try {
            $addrStmt = $pdo->prepare('SELECT tipo, logradouro, numero, complemento, bairro, cidade, estado, cep, latitude, longitude FROM fornecedor_addresses WHERE fornecedor_id = ? AND tipo IN ("principal","cobranca","entrega")');
            $addrStmt->execute([$id]);
            $addrs = $addrStmt->fetchAll();
            foreach ($addrs as $a) {
                if (($a['tipo'] ?? '') === 'principal') { $row['address'] = $a; }
                else if (($a['tipo'] ?? '') === 'cobranca') { $row['billing_address'] = $a; }
                else if (($a['tipo'] ?? '') === 'entrega') { $row['shipping_address'] = $a; }
            }
        } catch (\Throwable $e) { /* tabela pode não existir */ }
        AuditLogger::logRead('fornecedores', $id, ['endpoint' => 'fornecedores.get_by_id']);
        return Response::json(['data' => $row]);
    }

    public function create(Request $r): Response {
        $pdo = ConnectionManager::forTenant();
        $data = $r->body;
        $tipo = $data['tipo'] ?? 'PJ';
        $nome = trim((string)($data['nome'] ?? ($data['nome_fantasia'] ?? '')));
        $nomeFantasia = $data['nome_fantasia'] ?? '';
        $razaoSocial = $data['razao_social'] ?? '';
        $cpf = $data['cpf'] ?? null;
        $cnpj = $data['cnpj'] ?? null;
        $ie = $data['ie'] ?? null;
        $im = $data['im'] ?? null;
        $email = $data['email'] ?? null;
        $contato = $data['contato_nome'] ?? ($data['contato'] ?? null);
        $telefoneFixo = $data['telefone_fixo'] ?? null;
        $telefoneCel = $data['telefone_celular'] ?? null;
        $site = $data['site_url'] ?? null;
        $refs = $data['referencias'] ?? null;
        $limite = isset($data['limite_credito']) ? floatval(str_replace([','], ['.'], (string)$data['limite_credito'])) : null;
        $obs = $data['observacoes'] ?? null;
        $bloq = filter_var(($data['bloqueado'] ?? false), FILTER_VALIDATE_BOOLEAN);
        $inat = filter_var(($data['inativo'] ?? false), FILTER_VALIDATE_BOOLEAN);
        $motivo = $data['status_motivo'] ?? null;

        if ($tipo === 'PF') {
            $cpf = preg_replace('/\D/', '', (string)$cpf);
            if ($cpf && !Br::isCPF($cpf)) return new Response(422, ['error' => ['code' => 'invalid_cpf', 'message' => 'CPF inválido']]);
            $cnpj = null;
        } else {
            $cnpj = preg_replace('/\D/', '', (string)$cnpj);
            if (!$cnpj || !Br::isCNPJ($cnpj)) return new Response(422, ['error' => ['code' => 'invalid_cnpj', 'message' => 'CNPJ inválido']]);
            $cpf = null;
        }
        if ($email && !filter_var($email, FILTER_VALIDATE_EMAIL)) return new Response(422, ['error' => ['code' => 'invalid_email']]);

        // CNPJ único
        if ($cnpj) {
            $chk = $pdo->prepare('SELECT id FROM fornecedores WHERE cnpj = ? LIMIT 1');
            $chk->execute([$cnpj]);
            if ($chk->fetch()) {
                return new Response(409, ['error' => ['code' => 'duplicate_cnpj', 'message' => 'CNPJ já cadastrado']]);
            }
        }

        $stmt = $pdo->prepare('INSERT INTO fornecedores (nome, nome_fantasia, razao_social, tipo, cpf, cnpj, ie, im, telefone_fixo, telefone_celular, referencias, limite_credito, email, contato_nome, site_url, observacoes, bloqueado, inativo, status_motivo, created_at, updated_at) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,NOW(),NOW())');
        $stmt->execute([$nome, $nomeFantasia, $razaoSocial, $tipo, $cpf, $cnpj, $ie, $im, $telefoneFixo, $telefoneCel, $refs, $limite, $email, $contato, $site, $obs, $bloq ? 1 : 0, $inat ? 1 : 0, $motivo]);
        $id = (int)$pdo->lastInsertId();

        // Endereços
        $addrStmt = $pdo->prepare('INSERT INTO fornecedor_addresses (fornecedor_id, tipo, logradouro, numero, complemento, bairro, cidade, estado, cep, latitude, longitude) VALUES (?,?,?,?,?,?,?,?,?,?,?)');
        $addr = $data['address'] ?? null;
        if (is_array($addr)) {
            $addrStmt->execute([$id,'principal',$addr['logradouro'] ?? null,$addr['numero'] ?? null,$addr['complemento'] ?? null,$addr['bairro'] ?? null,$addr['cidade'] ?? null, strtoupper((string)($addr['estado'] ?? '')),$addr['cep'] ?? null,$addr['latitude'] ?? null,$addr['longitude'] ?? null]);
        }
        $bill = $data['billing_address'] ?? null;
        if (is_array($bill)) {
            $addrStmt->execute([$id,'cobranca',$bill['logradouro'] ?? null,$bill['numero'] ?? null,$bill['complemento'] ?? null,$bill['bairro'] ?? null,$bill['cidade'] ?? null, strtoupper((string)($bill['estado'] ?? '')),$bill['cep'] ?? null,$bill['latitude'] ?? null,$bill['longitude'] ?? null]);
        }
        $ship = $data['shipping_address'] ?? null;
        if (is_array($ship)) {
            $addrStmt->execute([$id,'entrega',$ship['logradouro'] ?? null,$ship['numero'] ?? null,$ship['complemento'] ?? null,$ship['bairro'] ?? null,$ship['cidade'] ?? null, strtoupper((string)($ship['estado'] ?? '')),$ship['cep'] ?? null,$ship['latitude'] ?? null,$ship['longitude'] ?? null]);
        }

        // Uploads (imagem, arquivo) - semelhante aos clientes
        $uploads = [
            'imagem' => ['max' => 2 * 1024 * 1024, 'ext' => ['jpg','jpeg','png','webp']],
            'arquivo' => ['max' => 10 * 1024 * 1024, 'ext' => ['pdf','doc','docx','xls','xlsx','zip']]
        ];
        $uploadDir = __DIR__ . '/../../../public/uploads/suppliers';
        if (!is_dir($uploadDir)) { @mkdir($uploadDir, 0775, true); }
        foreach ($uploads as $field => $rules) {
            if (!empty($r->files[$field]) && ($r->files[$field]['error'] ?? UPLOAD_ERR_NO_FILE) === UPLOAD_ERR_OK) {
                $f = $r->files[$field];
                $size = (int)($f['size'] ?? 0);
                $name = (string)($f['name'] ?? '');
                $tmp = (string)($f['tmp_name'] ?? '');
                $ext = strtolower(pathinfo($name, PATHINFO_EXTENSION));
                if ($size > $rules['max']) return new Response(413, ['error' => ['code' => 'file_too_large', 'message' => "Arquivo {$field} excede o limite"]]);
                if (!in_array($ext, $rules['ext'], true)) return new Response(422, ['error' => ['code' => 'invalid_file_ext', 'message' => "Extensão não suportada em {$field}"]]);
                $destName = uniqid("supplier_{$id}_{$field}_") . '.' . $ext;
                $dest = $uploadDir . '/' . $destName;
                if (!@move_uploaded_file($tmp, $dest)) return new Response(500, ['error' => ['code' => 'upload_failed', 'message' => "Falha ao salvar {$field}"]]);
                $url = '/uploads/suppliers/' . $destName;
                $col = $field === 'imagem' ? 'imagem_url' : 'arquivo_url';
                $pdo->prepare("UPDATE fornecedores SET {$col} = ? WHERE id = ?")->execute([$url, $id]);
            }
        }

        AuditLogger::logCreate('fornecedores', $id, $data);
        return Response::json(['ok' => true, 'id' => $id]);
    }

    public function update(Request $r): Response {
        $pdo = ConnectionManager::forTenant();
        $id = (int)($r->get('params')[0] ?? 0);
        if ($id <= 0) return new Response(422, ['error' => ['code' => 'invalid_id']]);
        $data = $r->body;
        $tipo = $data['tipo'] ?? 'PJ';
        $cpf = $data['cpf'] ?? null;
        $cnpj = $data['cnpj'] ?? null;
        if ($tipo === 'PF') {
            $cpf = preg_replace('/\D/', '', (string)$cpf);
            if ($cpf && !Br::isCPF($cpf)) return new Response(422, ['error' => ['code' => 'invalid_cpf']]);
            $cnpj = null;
        } else {
            $cnpj = preg_replace('/\D/', '', (string)$cnpj);
            if (!$cnpj || !Br::isCNPJ($cnpj)) return new Response(422, ['error' => ['code' => 'invalid_cnpj']]);
            $cpf = null;
        }
        $email = $data['email'] ?? null;
        if ($email && !filter_var($email, FILTER_VALIDATE_EMAIL)) return new Response(422, ['error' => ['code' => 'invalid_email']]);

        // CNPJ único na atualização
        if ($cnpj) {
            $chk = $pdo->prepare('SELECT id FROM fornecedores WHERE cnpj = ? AND id <> ? LIMIT 1');
            $chk->execute([$cnpj, $id]);
            if ($chk->fetch()) {
                return new Response(409, ['error' => ['code' => 'duplicate_cnpj', 'message' => 'CNPJ já cadastrado']]);
            }
        }

        $stmt = $pdo->prepare('UPDATE fornecedores SET nome = ?, nome_fantasia = ?, razao_social = ?, tipo = ?, cpf = ?, cnpj = ?, ie = ?, im = ?, telefone_fixo = ?, telefone_celular = ?, referencias = ?, limite_credito = ?, email = ?, contato_nome = ?, site_url = ?, observacoes = ?, bloqueado = ?, inativo = ?, status_motivo = ?, updated_at = NOW() WHERE id = ?');
        $stmt->execute([
            $data['nome'] ?? ($data['nome_fantasia'] ?? ''),
            $data['nome_fantasia'] ?? '',
            $data['razao_social'] ?? '',
            $tipo,
            $cpf,
            $cnpj,
            $data['ie'] ?? null,
            $data['im'] ?? null,
            $data['telefone_fixo'] ?? null,
            $data['telefone_celular'] ?? null,
            $data['referencias'] ?? null,
            isset($data['limite_credito']) ? floatval(str_replace([','], ['.'], (string)$data['limite_credito'])) : null,
            $email,
            $data['contato_nome'] ?? ($data['contato'] ?? null),
            $data['site_url'] ?? null,
            $data['observacoes'] ?? null,
            filter_var(($data['bloqueado'] ?? false), FILTER_VALIDATE_BOOLEAN) ? 1 : 0,
            filter_var(($data['inativo'] ?? false), FILTER_VALIDATE_BOOLEAN) ? 1 : 0,
            $data['status_motivo'] ?? null,
            $id
        ]);

        // Atualizar/Inserir endereços
        $selAddr = $pdo->prepare('SELECT tipo FROM fornecedor_addresses WHERE fornecedor_id = ? AND tipo = ? LIMIT 1');
        $insAddr = $pdo->prepare('INSERT INTO fornecedor_addresses (fornecedor_id, tipo, logradouro, numero, complemento, bairro, cidade, estado, cep, latitude, longitude) VALUES (?,?,?,?,?,?,?,?,?,?,?)');
        $updAddr = $pdo->prepare('UPDATE fornecedor_addresses SET logradouro = ?, numero = ?, complemento = ?, bairro = ?, cidade = ?, estado = ?, cep = ?, latitude = ?, longitude = ? WHERE fornecedor_id = ? AND tipo = ?');
        foreach ([['principal','address'],['cobranca','billing_address'],['entrega','shipping_address']] as [$tipoAddr, $key]) {
            $addr = $data[$key] ?? null;
            if (!is_array($addr)) continue;
            $selAddr->execute([$id, $tipoAddr]);
            if ($selAddr->fetch()) {
                $updAddr->execute([
                    $addr['logradouro'] ?? null,
                    $addr['numero'] ?? null,
                    $addr['complemento'] ?? null,
                    $addr['bairro'] ?? null,
                    $addr['cidade'] ?? null,
                    strtoupper((string)($addr['estado'] ?? '')),
                    $addr['cep'] ?? null,
                    $addr['latitude'] ?? null,
                    $addr['longitude'] ?? null,
                    $id,
                    $tipoAddr
                ]);
            } else {
                $insAddr->execute([$id,$tipoAddr,$addr['logradouro'] ?? null,$addr['numero'] ?? null,$addr['complemento'] ?? null,$addr['bairro'] ?? null,$addr['cidade'] ?? null,strtoupper((string)($addr['estado'] ?? '')),$addr['cep'] ?? null,$addr['latitude'] ?? null,$addr['longitude'] ?? null]);
            }
        }

        // Uploads (imagem, arquivo) - semelhante aos clientes
        $uploads = [
            'imagem' => ['max' => 2 * 1024 * 1024, 'ext' => ['jpg','jpeg','png','webp']],
            'arquivo' => ['max' => 10 * 1024 * 1024, 'ext' => ['pdf','doc','docx','xls','xlsx','zip']]
        ];
        $uploadDir = __DIR__ . '/../../../public/uploads/suppliers';
        if (!is_dir($uploadDir)) { @mkdir($uploadDir, 0775, true); }
        foreach ($uploads as $field => $rules) {
            if (!empty($r->files[$field]) && ($r->files[$field]['error'] ?? UPLOAD_ERR_NO_FILE) === UPLOAD_ERR_OK) {
                $f = $r->files[$field];
                $size = (int)($f['size'] ?? 0);
                $name = (string)($f['name'] ?? '');
                $tmp = (string)($f['tmp_name'] ?? '');
                $ext = strtolower(pathinfo($name, PATHINFO_EXTENSION));
                if ($size > $rules['max']) return new Response(413, ['error' => ['code' => 'file_too_large', 'message' => "Arquivo {$field} excede o limite"]]);
                if (!in_array($ext, $rules['ext'], true)) return new Response(422, ['error' => ['code' => 'invalid_file_ext', 'message' => "Extensão não suportada em {$field}"]]);

                // Remover arquivo antigo, se existir
                $col = $field === 'imagem' ? 'imagem_url' : 'arquivo_url';
                $oldStmt = $pdo->prepare("SELECT {$col} FROM fornecedores WHERE id = ?");
                $oldStmt->execute([$id]);
                $old = $oldStmt->fetch();
                if ($old && !empty($old[$col])) {
                    $oldPath = __DIR__ . '/../../../public' . $old[$col];
                    if (file_exists($oldPath)) { @unlink($oldPath); }
                }

                $destName = uniqid("supplier_{$id}_{$field}_") . '.' . $ext;
                $dest = $uploadDir . '/' . $destName;
                if (!@move_uploaded_file($tmp, $dest)) return new Response(500, ['error' => ['code' => 'upload_failed', 'message' => "Falha ao salvar {$field}"]]);
                $url = '/uploads/suppliers/' . $destName;
                $pdo->prepare("UPDATE fornecedores SET {$col} = ? WHERE id = ?")->execute([$url, $id]);
            }
        }

        AuditLogger::logUpdate('fornecedores', $id, $data);
        return Response::json(['ok' => true]);
    }

    public function delete(Request $r): Response {
        $pdo = ConnectionManager::forTenant();
        $id = (int)($r->get('params')[0] ?? 0);
        if ($id <= 0) return new Response(422, ['error' => ['code' => 'invalid_id']]);
        $pdo->prepare('DELETE FROM fornecedor_addresses WHERE fornecedor_id = ?')->execute([$id]);
        $pdo->prepare('DELETE FROM fornecedores WHERE id = ?')->execute([$id]);
        AuditLogger::logDelete('fornecedores', $id);
        return Response::json(['ok' => true]);
    }

    // Views
    public function viewIndex(): Response { return View::renderWithLayout('fornecedores/index', ['title' => 'Fornecedores']); }
    public function viewShow(): Response { return View::renderWithLayout('fornecedores/show', ['title' => 'Fornecedor']); }
    public function viewCreate(): Response { return View::renderWithLayout('fornecedores/create', ['title' => 'Novo Fornecedor']); }
    public function viewEdit(): Response { return View::renderWithLayout('fornecedores/edit', ['title' => 'Editar Fornecedor']); }
}