import 'dotenv/config';
import express from 'express';
import cors from 'cors';
import crypto from 'node:crypto';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
import fs from 'node:fs';
import { createPool } from './db.js';
import bcrypt from 'bcryptjs';
import jwt from 'jsonwebtoken';
import multer from 'multer';

const app = express();
app.use(cors());
app.use(express.json());

const PORT = process.env.PORT || 4000;
const pool = createPool();

// Static directory for uploads
const __dirname = path.dirname(fileURLToPath(import.meta.url));
const uploadsDir = path.join(__dirname, '..', 'uploads');
fs.mkdirSync(uploadsDir, { recursive: true });
app.use('/uploads', express.static(uploadsDir));

// Multer setup for photo upload
const storage = multer.diskStorage({
  destination: (_req, _file, cb) => cb(null, uploadsDir),
  filename: (req, file, cb) => {
    const ext = path.extname(file.originalname).toLowerCase();
    const name = `${req.user?.sub || crypto.randomUUID()}-${Date.now()}${ext}`;
    cb(null, name);
  },
});
const imageFilter = (_req, file, cb) => {
  const allowed = ['.jpg', '.jpeg', '.png', '.gif', '.webp'];
  const ext = path.extname(file.originalname).toLowerCase();
  if (allowed.includes(ext)) cb(null, true);
  else cb(new Error('Formato de imagem não suportado'));
};
const upload = multer({ storage, fileFilter: imageFilter, limits: { fileSize: 5 * 1024 * 1024 } });

// DB initialization via migrations (Sequelize CLI)
// As tabelas agora são gerenciadas por migrations. Execute:
//   npm run db:migrate
// para preparar o schema.

// Health check
app.get('/api/health', async (_req, res) => {
  try {
    await pool.query('SELECT 1');
    res.json({ ok: true });
  } catch (err) {
    res.status(500).json({ ok: false, error: err.message });
  }
});

// Middleware simples de autenticação via JWT
function authenticate(req, res, next) {
  try {
    const auth = req.headers.authorization || '';
    const parts = auth.split(' ');
    if (parts.length !== 2 || parts[0] !== 'Bearer') {
      return res.status(401).json({ error: 'Não autenticado' });
    }
    const token = parts[1];
    const secret = process.env.JWT_SECRET || 'devsecret';
    const payload = jwt.verify(token, secret);
    req.user = payload; // { sub, role, iat, exp }
    next();
  } catch (err) {
    return res.status(401).json({ error: 'Token inválido' });
  }
}

// Auth: Login
app.post('/api/auth/login', async (req, res) => {
  try {
    const { email, password } = req.body || {};
    if (!email || !password) {
      return res.status(400).json({ error: 'E-mail e senha são obrigatórios.' });
    }

    const [rows] = await pool.query(
      'SELECT id, nome, email, role, password_hash FROM users WHERE email = ?',
      [String(email).trim().toLowerCase()]
    );

    if (!rows || rows.length === 0) {
      return res.status(401).json({ error: 'Credenciais inválidas.' });
    }

    const user = rows[0];
    const ok = await bcrypt.compare(String(password), user.password_hash);
    if (!ok) {
      return res.status(401).json({ error: 'Credenciais inválidas.' });
    }

    const secret = process.env.JWT_SECRET || 'devsecret';
    const token = jwt.sign({ sub: user.id, role: user.role }, secret, { expiresIn: '8h' });

    res.json({
      token,
      user: { id: user.id, nome: user.nome, email: user.email, role: user.role },
    });
  } catch (err) {
    res.status(500).json({ error: err.message });
  }
});

// Users: criação (somente Master)
app.post('/api/users', authenticate, async (req, res) => {
  try {
    const actor = req.user;
    if (!actor || actor.role !== 'Master') {
      return res.status(403).json({ error: 'Permissão negada' });
    }

    const { nome, email, role, password } = req.body || {};
    if (!nome || !email || !role || !password) {
      return res.status(400).json({ error: 'Campos obrigatórios: nome, email, role, password' });
    }

    const emailNormalized = String(email).trim().toLowerCase();

    const [exists] = await pool.query('SELECT id FROM users WHERE email = ?', [emailNormalized]);
    if (exists.length > 0) {
      return res.status(409).json({ error: 'E-mail já cadastrado' });
    }

    const id = crypto.randomUUID();
    const passwordHash = await bcrypt.hash(String(password), 10);
    await pool.query(
      'INSERT INTO users (id, nome, email, role, password_hash) VALUES (?, ?, ?, ?, ?)',
      [id, String(nome).trim(), emailNormalized, String(role).trim(), passwordHash]
    );

    res.status(201).json({ id, nome: String(nome).trim(), email: emailNormalized, role: String(role).trim() });
  } catch (err) {
    res.status(500).json({ error: err.message });
  }
});

// Users: perfil do próprio usuário
app.get('/api/users/me', authenticate, async (req, res) => {
  try {
    const userId = req.user.sub;
    const [rows] = await pool.query('SELECT id, nome, email, role, photo_url FROM users WHERE id = ?', [userId]);
    if (!rows || rows.length === 0) return res.status(404).json({ error: 'Usuário não encontrado' });
    return res.json(rows[0]);
  } catch (err) {
    res.status(500).json({ error: err.message });
  }
});

app.put('/api/users/me', authenticate, async (req, res) => {
  try {
    const userId = req.user.sub;
    const { nome, email } = req.body || {};
    if (!nome && !email) return res.status(400).json({ error: 'Nada para atualizar' });

    if (email) {
      const emailNorm = String(email).trim().toLowerCase();
      const [exists] = await pool.query('SELECT id FROM users WHERE email = ? AND id <> ?', [emailNorm, userId]);
      if (exists.length > 0) return res.status(409).json({ error: 'E-mail já em uso' });
      await pool.query('UPDATE users SET nome = COALESCE(?, nome), email = COALESCE(?, email), updated_at = CURRENT_TIMESTAMP WHERE id = ?', [nome || null, emailNorm, userId]);
    } else {
      await pool.query('UPDATE users SET nome = COALESCE(?, nome), updated_at = CURRENT_TIMESTAMP WHERE id = ?', [nome || null, userId]);
    }

    const [rows] = await pool.query('SELECT id, nome, email, role, photo_url FROM users WHERE id = ?', [userId]);
    return res.json(rows[0]);
  } catch (err) {
    res.status(500).json({ error: err.message });
  }
});

app.post('/api/users/me/password', authenticate, async (req, res) => {
  try {
    const userId = req.user.sub;
    const { currentPassword, newPassword } = req.body || {};
    if (!currentPassword || !newPassword) return res.status(400).json({ error: 'Informe a senha atual e a nova senha' });

    const [rows] = await pool.query('SELECT password_hash FROM users WHERE id = ?', [userId]);
    if (!rows || rows.length === 0) return res.status(404).json({ error: 'Usuário não encontrado' });
    const ok = await bcrypt.compare(String(currentPassword), rows[0].password_hash);
    if (!ok) return res.status(401).json({ error: 'Senha atual incorreta' });

    const newHash = await bcrypt.hash(String(newPassword), 10);
    await pool.query('UPDATE users SET password_hash = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ?', [newHash, userId]);
    return res.json({ ok: true });
  } catch (err) {
    res.status(500).json({ error: err.message });
  }
});

app.post('/api/users/me/photo', authenticate, upload.single('photo'), async (req, res) => {
  try {
    if (!req.file) return res.status(400).json({ error: 'Arquivo de foto é obrigatório' });
    const relPath = `/uploads/${req.file.filename}`;
    await pool.query('UPDATE users SET photo_url = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ?', [relPath, req.user.sub]);
    return res.json({ photo_url: relPath });
  } catch (err) {
    res.status(500).json({ error: err.message });
  }
});

// Fornecedores
app.get('/api/fornecedores', async (_req, res) => {
  try {
    const [rows] = await pool.query('SELECT id, nome FROM fornecedores ORDER BY nome ASC');
    res.json(rows);
  } catch (err) {
    res.status(500).json({ error: err.message });
  }
});

app.post('/api/fornecedores', async (req, res) => {
  try {
    const { nome } = req.body || {};
    if (!nome || typeof nome !== 'string') {
      return res.status(400).json({ error: 'Nome do fornecedor é obrigatório.' });
    }
    const id = crypto.randomUUID();
    await pool.query('INSERT INTO fornecedores (id, nome) VALUES (?, ?)', [id, nome.trim().toUpperCase()]);
    res.status(201).json({ id, nome: nome.trim().toUpperCase() });
  } catch (err) {
    res.status(500).json({ error: err.message });
  }
});

app.put('/api/fornecedores/:id', async (req, res) => {
  try {
    const { id } = req.params;
    const { nome } = req.body || {};
    if (!nome) return res.status(400).json({ error: 'Nome é obrigatório.' });
    await pool.query('UPDATE fornecedores SET nome = ? WHERE id = ?', [nome.trim().toUpperCase(), id]);
    res.json({ id, nome: nome.trim().toUpperCase() });
  } catch (err) {
    res.status(500).json({ error: err.message });
  }
});

app.delete('/api/fornecedores/:id', async (req, res) => {
  try {
    const { id } = req.params;
    await pool.query('DELETE FROM fornecedores WHERE id = ?', [id]);
    res.status(204).send();
  } catch (err) {
    res.status(500).json({ error: err.message });
  }
});

// Notas Fiscais
app.get('/api/nfs', async (_req, res) => {
  try {
    const [rows] = await pool.query(`
      SELECT nf.*, f.nome AS fornecedor_nome
      FROM notas_fiscais nf
      JOIN fornecedores f ON nf.fornecedor_id = f.id
      ORDER BY nf.data_emissao DESC
    `);
    res.json(rows);
  } catch (err) {
    res.status(500).json({ error: err.message });
  }
});

app.post('/api/nfs', async (req, res) => {
  try {
    const {
      numeroNF,
      numeroPedido,
      fornecedor, // nome do fornecedor
      dataPrevisao,
      dataEmissao,
      numeroCTE,
      observacao,
    } = req.body || {};

    if (!numeroNF || !numeroPedido || !fornecedor || !dataPrevisao || !dataEmissao) {
      return res.status(400).json({ error: 'Campos obrigatórios faltando.' });
    }

    const fornecedorNome = String(fornecedor).trim().toUpperCase();
    // Upsert fornecedor
    const [found] = await pool.query('SELECT id FROM fornecedores WHERE nome = ?', [fornecedorNome]);
    let fornecedorId;
    if (found.length > 0) {
      fornecedorId = found[0].id;
    } else {
      fornecedorId = crypto.randomUUID();
      await pool.query('INSERT INTO fornecedores (id, nome) VALUES (?, ?)', [fornecedorId, fornecedorNome]);
    }

    const id = crypto.randomUUID();
    const status = 'Pendente';
    await pool.query(
      `INSERT INTO notas_fiscais (
        id, numero_nf, numero_pedido, fornecedor_id, data_previsao, data_emissao, numero_cte, observacao, status
      ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`,
      [id, numeroNF, numeroPedido, fornecedorId, dataPrevisao, dataEmissao, numeroCTE || null, observacao || null, status]
    );

    // history inicial
    await pool.query(
      'INSERT INTO nf_history (nf_id, status, date, user) VALUES (?, ?, NOW(), ?)',
      [id, status, 'Diligenciador']
    );

    res.status(201).json({ id });
  } catch (err) {
    res.status(500).json({ error: err.message });
  }
});

// GRDs
app.get('/api/grds', async (_req, res) => {
  try {
    const [grds] = await pool.query(
      'SELECT id, codigo, data_emissao, observacao FROM grds ORDER BY CAST(codigo AS UNSIGNED) DESC'
    );

    if (!grds || grds.length === 0) return res.json([]);

    const ids = grds.map(g => g.id);
    const [links] = await pool.query(
      'SELECT grd_id, nf_id FROM grd_nf WHERE grd_id IN (?)',
      [ids]
    );
    const map = new Map();
    for (const g of grds) map.set(g.id, []);
    for (const l of links) {
      const arr = map.get(l.grd_id);
      if (arr) arr.push(l.nf_id);
    }

    const payload = grds.map(g => ({
      id: g.id,
      codigo: g.codigo,
      data_emissao: g.data_emissao,
      observacao: g.observacao || null,
      nf_ids: map.get(g.id) || [],
    }));
    res.json(payload);
  } catch (err) {
    res.status(500).json({ error: err.message });
  }
});

app.post('/api/grds', async (req, res) => {
  try {
    const { nfIds, observacao } = req.body || {};
    if (!Array.isArray(nfIds) || nfIds.length === 0) {
      return res.status(400).json({ error: 'Selecione ao menos uma NF para gerar o GRD.' });
    }

    // calcula próximo código numérico
    const [rows] = await pool.query('SELECT MAX(CAST(codigo AS UNSIGNED)) AS maxCodigo FROM grds');
    const nextNumber = (rows && rows[0] && rows[0].maxCodigo) ? Number(rows[0].maxCodigo) + 1 : 819;

    const id = crypto.randomUUID();
    const codigo = String(nextNumber);
    await pool.query(
      'INSERT INTO grds (id, codigo, data_emissao, observacao) VALUES (?, ?, NOW(), ?)',
      [id, codigo, observacao || null]
    );

    // Verifica se todos os nfIds existem
    const [existing] = await pool.query('SELECT id FROM notas_fiscais WHERE id IN (?)', [nfIds]);
    const existingIds = new Set(existing.map(r => r.id));
    const missing = nfIds.filter(nfId => !existingIds.has(nfId));
    if (missing.length > 0) {
      return res.status(400).json({ error: 'Algumas NFs não existem no banco.', missing });
    }

    // Insere vínculos (bulk insert com placeholders)
    const placeholders = nfIds.map(() => '(?, ?)').join(', ');
    const params = nfIds.flatMap(nfId => [id, nfId]);
    await pool.query(`INSERT INTO grd_nf (grd_id, nf_id) VALUES ${placeholders}`, params);

    res.status(201).json({ id, codigo, data_emissao: new Date(), observacao: observacao || null, nf_ids: nfIds });
  } catch (err) {
    console.error('POST /api/grds error:', err);
    res.status(500).json({ error: err.message });
  }
});

app.listen(PORT, () => {
  console.log(`API server listening on http://localhost:${PORT}`);
});