'use strict'

const bcrypt = require('bcryptjs')
const crypto = require('crypto')
const User   = require('../models/User')
const Claim  = require('../models/Claim')
const Notification       = require('../models/Notification')
const NotificationService = require('../services/notificationService')
const { queryOne, execute } = require('../database/postgres')
const { signUserToken }     = require('../middleware/auth')

const COOKIE_SECURE = process.env.COOKIE_SECURE === 'true'

const COOKIE_OPTS = {
  httpOnly: true,
  maxAge: 24 * 60 * 60 * 1000,
  secure: COOKIE_SECURE,
  sameSite: 'lax',
}
// Cookie hint legível pelo JS — permite o frontend evitar chamada desnecessária a /me
const HINT_OPTS = {
  httpOnly: false,
  maxAge: 24 * 60 * 60 * 1000,
  secure: COOKIE_SECURE,
  sameSite: 'lax',
}
function setUserCookies(res, token) {
  res.cookie('user_token', token, COOKIE_OPTS)
  res.cookie('_usr', '1', HINT_OPTS)
}

const usersController = {

  // ── Lead (hotspot) ─────────────────────────────────────────────────────────
  async receiveLead(req, res) {
    try {
      const { nome, email, cpf, telefone, ip, source, session_id } = req.body
      if (!email && !nome) return res.status(400).json({ error: 'Nome ou e-mail é obrigatório' })

      const tenantId    = req.tenant?.id || 1
      const cleanEmail  = email ? email.toLowerCase().trim() : null
      const cleanCpf    = cpf ? cpf.replace(/[^\d]+/g, '') : null
      const cleanTel    = telefone ? telefone.replace(/[^\d]+/g, '') : null
      const clientIp    = ip || req.ip || null
      const displayNome = nome || cleanEmail

      // Deduplicação: email primeiro (mais confiável), depois telefone
      let existing = null
      if (cleanEmail) existing = await User.findByEmail(cleanEmail, tenantId)
      if (!existing && cleanTel) existing = await User.findByPhone(cleanTel, tenantId)

      if (existing) {
        await execute(
          `UPDATE users
              SET ip = $1, portal_session_id = $2, timestamp = NOW()
            WHERE id = $3`,
          [clientIp, session_id || null, existing.id]
        )
        console.log(`📥 Lead atualizado (${cleanEmail || cleanTel}): id=${existing.id}`)
        return res.json({ success: true, id: existing.id, updated: true })
      }

      const result = await User.create({
        nome:              displayNome,
        email:             cleanEmail,
        cpf:               cleanCpf,
        telefone:          cleanTel,
        ip:                clientIp,
        source:            source || 'hotspot',
        tenant_id:         tenantId,
        portal_session_id: session_id || null,
      })
      console.log('📥 Lead criado:', cleanEmail || displayNome, session_id ? `(session: ${session_id})` : '')
      return res.json({ success: true, id: result.id ?? result.lastInsertRowid })
    } catch (error) {
      console.error('Erro ao receber lead:', error)
      return res.status(500).json({ error: 'Erro interno' })
    }
  },

  // ── Admin ──────────────────────────────────────────────────────────────────
  async getAll(req, res) {
    try {
      return res.json(await User.findAll(req.tenant?.id))
    } catch { return res.status(500).json({ error: 'Erro interno' }) }
  },

  async getStats(req, res) {
    try {
      return res.json(await User.getStats(req.tenant?.id))
    } catch { return res.status(500).json({ error: 'Erro interno' }) }
  },

  async deleteUser(req, res) {
    try {
      await User.deleteById(req.params.id)
      return res.json({ success: true })
    } catch { return res.status(500).json({ error: 'Erro interno' }) }
  },

  async updateUser(req, res) {
    try {
      const { nome, email, cpf, telefone, source } = req.body
      await User.update(req.params.id, { nome, email, cpf, telefone, source })
      return res.json({ success: true })
    } catch (e) {
      console.error('Erro ao atualizar usuário:', e)
      return res.status(500).json({ error: 'Erro interno' })
    }
  },

  // ── Cadastro ───────────────────────────────────────────────────────────────
  async registerUser(req, res) {
    try {
      const { nome, email, cpf, telefone, password } = req.body
      if (!nome || !email || !cpf || !telefone || !password) {
        return res.status(400).json({ error: 'Nome, email, CPF, telefone e senha são obrigatórios' })
      }
      if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) {
        return res.status(400).json({ error: 'E-mail inválido' })
      }
      if (password.length < 6) {
        return res.status(400).json({ error: 'Senha deve ter ao menos 6 caracteres' })
      }

      const tenantId = req.tenant?.id || 1
      const existing = await User.findByEmail(email, tenantId)
      if (existing) {
        return res.status(409).json({ error: 'E-mail já cadastrado. Faça login.' })
      }

      const password_hash = await bcrypt.hash(password, 10)
      const result = await User.create({ nome, email, cpf, telefone, source: 'web', password_hash, tenant_id: tenantId })
      const newId  = Number(result.lastInsertRowid ?? result.rows?.[0]?.id)

      const token = signUserToken({ id: newId, nome, cpf, email }, tenantId)
      setUserCookies(res, token)

      NotificationService.notifyWelcome(newId, nome, telefone, cpf).catch(() => {})
      return res.status(201).json({ success: true, redirect: '/area-do-usuario' })
    } catch (error) {
      console.error('Erro no cadastro:', error)
      if (error.code === '23505') return res.status(409).json({ error: 'E-mail já cadastrado.' })
      return res.status(500).json({ error: 'Erro interno' })
    }
  },

  // ── Login ──────────────────────────────────────────────────────────────────
  async loginUser(req, res) {
    try {
      const { email, password, cpf, telefone } = req.body
      const tenantId = req.tenant?.id || 1
      let user = null

      if (email && password) {
        // Fluxo novo: email + senha
        if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) {
          return res.status(400).json({ error: 'E-mail inválido' })
        }
        user = await User.findByEmailAndTenant(email, tenantId)
        if (!user) return res.status(401).json({ error: 'E-mail não encontrado.' })
        if (!user.password_hash) {
          return res.status(401).json({
            error: 'Conta legada. Use CPF e telefone ou migre sua conta.',
            legacy: true,
          })
        }
        const ok = await bcrypt.compare(password, user.password_hash)
        if (!ok) return res.status(401).json({ error: 'Senha incorreta.' })

      } else if (cpf && telefone) {
        // Fallback legado: CPF + telefone (apenas se sem password_hash)
        user = await User.findByCpfPhone(cpf, telefone, tenantId)
        if (!user) return res.status(401).json({ error: 'Usuário não encontrado. Verifique os dados ou cadastre-se.' })
        if (user.password_hash) {
          return res.status(401).json({ error: 'Esta conta usa email e senha. Faça login com seu e-mail.' })
        }
      } else {
        return res.status(400).json({ error: 'Informe email+senha ou CPF+telefone.' })
      }

      const token = signUserToken(user, tenantId)
      setUserCookies(res, token)

      if (user.telefone) {
        NotificationService.notifySecurityLogin(user.id, user.nome, user.telefone, user.cpf).catch(() => {})
      }
      const safeUser = {
        id: user.id,
        nome: user.nome,
        email: user.email,
        cpf: user.cpf,
        telefone: user.telefone,
        whatsapp_optin: user.whatsapp_optin,
      }
      return res.json({ success: true, redirect: '/area-do-usuario', user: safeUser })
    } catch (error) {
      console.error('Erro no login:', error)
      return res.status(500).json({ error: 'Erro interno' })
    }
  },

  // ── Esqueci a senha ────────────────────────────────────────────────────────
  async forgotPassword(req, res) {
    try {
      const { email } = req.body
      if (!email) return res.status(400).json({ error: 'E-mail obrigatório' })
      const tenantId = req.tenant?.id || 1
      const user = await User.findByEmailAndTenant(email, tenantId)
      // Responde sempre 200 para não revelar se o email existe
      if (!user) return res.json({ ok: true })

      const token     = crypto.randomBytes(48).toString('hex')
      const expiresAt = new Date(Date.now() + 60 * 60 * 1000) // 1h
      await execute(
        `INSERT INTO password_reset_tokens (user_id, token, expires_at) VALUES ($1, $2, $3)`,
        [user.id, token, expiresAt]
      )

      const appUrl = req.tenant?.app_url || process.env.APP_URL || 'http://localhost:4001'
      const link   = `${appUrl}/redefinir-senha?token=${token}`

      if (user.telefone) {
        NotificationService.notifyPasswordReset(user.id, user.nome, user.telefone, user.cpf, link).catch(() => {})
      }
      return res.json({ ok: true })
    } catch (e) {
      console.error('Erro forgot-password:', e)
      return res.status(500).json({ error: 'Erro interno' })
    }
  },

  // ── Redefinir senha ────────────────────────────────────────────────────────
  async resetPassword(req, res) {
    try {
      const { token, password } = req.body
      if (!token || !password) return res.status(400).json({ error: 'Token e senha obrigatórios' })
      if (password.length < 6) return res.status(400).json({ error: 'Senha deve ter ao menos 6 caracteres' })

      const row = await queryOne(
        `SELECT * FROM password_reset_tokens
         WHERE token=$1 AND used=FALSE AND expires_at > NOW()`,
        [token]
      )
      if (!row) return res.status(400).json({ error: 'Token inválido ou expirado' })

      const password_hash = await bcrypt.hash(password, 10)
      await User.updatePassword(row.user_id, password_hash)
      await execute(`UPDATE password_reset_tokens SET used=TRUE WHERE id=$1`, [row.id])

      return res.json({ ok: true })
    } catch (e) {
      console.error('Erro reset-password:', e)
      return res.status(500).json({ error: 'Erro interno' })
    }
  },

  // ── Ativação de lead do hotspot (sem CPF) ─────────────────────────────────
  async activateUser(req, res) {
    try {
      const { email, telefone, nome, password } = req.body
      if (!email || !telefone || !nome || !password) {
        return res.status(400).json({ error: 'Nome, e-mail, celular e senha são obrigatórios' })
      }
      if (password.length < 6) return res.status(400).json({ error: 'Senha deve ter ao menos 6 caracteres' })

      const tenantId = req.tenant?.id || 1
      const cleanEmail = email.toLowerCase().trim()
      const cleanTel   = telefone.replace(/\D/g, '')

      // Busca o lead pelo email (ou pelo telefone se email não bater)
      let user = await User.findByEmailAndTenant(cleanEmail, tenantId)
      if (!user) user = await User.findByPhone(cleanTel, tenantId)
      if (!user) return res.status(404).json({ error: 'Cadastro não encontrado. Conecte-se ao WiFi primeiro.' })
      if (user.password_hash) return res.status(409).json({ error: 'Conta já ativada. Faça login normalmente.' })

      const password_hash = await bcrypt.hash(password, 10)
      await execute(
        `UPDATE users SET nome=$1, email=$2, password_hash=$3 WHERE id=$4`,
        [nome, cleanEmail, password_hash, user.id]
      )

      const token = signUserToken({ ...user, nome, email: cleanEmail }, tenantId)
      setUserCookies(res, token)
      NotificationService.notifyWelcome(user.id, nome, user.telefone || cleanTel, user.cpf).catch(() => {})
      return res.status(200).json({ success: true, redirect: '/area-do-usuario' })
    } catch (e) {
      console.error('Erro activate:', e)
      return res.status(500).json({ error: 'Erro interno' })
    }
  },

  // ── Migração de conta legada ───────────────────────────────────────────────
  async migrateAccount(req, res) {
    try {
      const { cpf, telefone, email, password } = req.body
      if (!cpf || !telefone || !email || !password) {
        return res.status(400).json({ error: 'CPF, telefone, novo email e senha são obrigatórios' })
      }
      if (password.length < 6) return res.status(400).json({ error: 'Senha deve ter ao menos 6 caracteres' })

      const tenantId = req.tenant?.id || 1
      const user = await User.findByCpfPhone(cpf, telefone, tenantId)
      if (!user) return res.status(401).json({ error: 'Dados não encontrados.' })
      if (user.password_hash) return res.status(400).json({ error: 'Conta já migrada. Faça login com e-mail e senha.' })

      const emailExists = await User.findByEmailAndTenant(email, tenantId)
      if (emailExists && emailExists.id !== user.id) {
        return res.status(409).json({ error: 'Este e-mail já está em uso por outra conta.' })
      }

      const password_hash = await bcrypt.hash(password, 10)
      await execute(
        `UPDATE users SET email=$1, password_hash=$2 WHERE id=$3`,
        [email, password_hash, user.id]
      )

      const token = signUserToken({ ...user, email }, tenantId)
      setUserCookies(res, token)
      return res.json({ success: true, redirect: '/area-do-usuario' })
    } catch (e) {
      console.error('Erro migrate:', e)
      return res.status(500).json({ error: 'Erro interno' })
    }
  },

  // ── Dados do usuário logado ────────────────────────────────────────────────
  async getMe(req, res) {
    try {
      if (!req.user) return res.status(401).json({ error: 'Não autorizado' })
      const user = await queryOne(
        'SELECT id, nome, email, cpf, telefone, whatsapp_optin FROM users WHERE id=$1',
        [req.user.id]
      )
      if (!user) return res.status(404).json({ error: 'Usuário não encontrado' })
      return res.json(user)
    } catch { return res.status(500).json({ error: 'Erro interno' }) }
  },

  async updateMe(req, res) {
    try {
      if (!req.user) return res.status(401).json({ error: 'Não autorizado' })
      const { nome, email, telefone } = req.body
      if (!nome?.trim()) return res.status(400).json({ error: 'Nome é obrigatório' })
      const updated = await queryOne(
        `UPDATE users SET nome=$1, email=$2, telefone=$3, updated_at=NOW()
         WHERE id=$4 RETURNING id, nome, email, cpf, telefone, whatsapp_optin`,
        [nome.trim(), email?.trim() || null, telefone?.trim() || null, req.user.id]
      )
      return res.json(updated)
    } catch { return res.status(500).json({ error: 'Erro interno' }) }
  },

  async updateOptin(req, res) {
    try {
      if (!req.user) return res.status(401).json({ error: 'Não autorizado' })
      // Aceita tanto { whatsapp_optin } (front-end) quanto { optin } (legado)
      const optin = req.body.whatsapp_optin ?? req.body.optin
      if (typeof optin !== 'boolean') return res.status(400).json({ error: 'Campo "whatsapp_optin" (boolean) obrigatório' })
      await execute('UPDATE users SET whatsapp_optin=$1 WHERE id=$2', [optin, req.user.id])
      return res.json({ ok: true, whatsapp_optin: optin })
    } catch { return res.status(500).json({ error: 'Erro interno' }) }
  },

  // ── Notificações ───────────────────────────────────────────────────────────
  async getMyNotifications(req, res) {
    try {
      if (!req.user?.cpf) return res.status(401).json({ error: 'Não autorizado' })
      const notifs      = await Notification.getUserNotifications(req.user.cpf)
      const unreadCount = await Notification.getUnreadCount(req.user.cpf)
      return res.json({ unread: unreadCount, notifications: notifs.rows || notifs })
    } catch { res.status(500).json({ error: 'Erro interno' }) }
  },

  async markNotificationRead(req, res) {
    try {
      if (!req.user?.cpf) return res.status(401).json({ error: 'Não autorizado' })
      await Notification.markAsRead(req.params.id, req.user.cpf)
      return res.json({ success: true })
    } catch { res.status(500).json({ error: 'Erro interno' }) }
  },

  // ── Resgates ───────────────────────────────────────────────────────────────
  async getMyClaims(req, res) {
    try {
      if (!req.user?.cpf) return res.status(401).json({ error: 'Não autorizado' })
      const claims = await Claim.getUserClaims(req.user.cpf)
      return res.json(claims.rows || claims)
    } catch { res.status(500).json({ error: 'Erro interno' }) }
  },

  async createClaim(req, res) {
    try {
      if (!req.user?.cpf) return res.status(401).json({ error: 'Não autorizado' })
      const { raffle_id, ticket_id, delivery_address, delivery_notes } = req.body
      if (!raffle_id || !ticket_id) return res.status(400).json({ error: 'Faltam dados da rifa' })

      const claim   = await Claim.create({ raffle_id, ticket_id, user_cpf: req.user.cpf, delivery_address, delivery_notes })
      const claimId = claim.rows?.[0]?.id ?? claim.lastInsertRowid
      const details = await Claim.getClaimDetails(claimId)

      if (details) {
        const u = await queryOne('SELECT telefone, nome FROM users WHERE cpf=$1', [req.user.cpf])
        NotificationService.notifyClaimCreated(u?.telefone, req.user.cpf, u?.nome, details.raffle_titulo, details.ticket_numero).catch(() => {})
      }
      return res.json({ success: true, id: claimId })
    } catch { res.status(500).json({ error: 'Erro interno' }) }
  },

  /**
   * Captive portal: verifica se o email/telefone já tem cadastro completo.
   * Usado pelo portal local antes de redirecionar para a landing page.
   * Protegido por X-Portal-Key — não expõe dados sensíveis.
   */
  async checkPortalUser(req, res) {
    try {
      const { email, telefone } = req.body
      if (!email && !telefone) return res.json({ found: false })

      const tenantId = req.tenant?.id || 1
      let user = null

      if (email) {
        user = await User.findByEmailAndTenant(email.toLowerCase().trim(), tenantId)
      }

      if (!user && telefone) {
        const cleanTel = telefone.replace(/\D/g, '')
        // busca só por telefone quando não tem email ou email não bateu
        user = await queryOne(
          `SELECT id, nome, email FROM users WHERE REGEXP_REPLACE(telefone,'[^0-9]','','g') = $1 AND tenant_id = $2 LIMIT 1`,
          [cleanTel, tenantId]
        )
      }

      if (!user) return res.json({ found: false })

      // needs_password: lead do hotspot ainda não criou senha — site pode oferecer ativação
      return res.json({ found: true, nome: user.nome, needs_password: !user.password_hash })
    } catch (err) {
      console.error('checkPortalUser error:', err.message)
      // Em caso de erro, retorna found:false para não bloquear o portal
      return res.json({ found: false })
    }
  },
}

module.exports = usersController
