import { authenticate } from '../../plugins/auth.js'
import {
  getCustomerById,
  markCustomerUspSeen,
  updateCustomerDisplayName,
} from '../../services/customer.service.js'
import {
  completeCustomerPhoneSignIn,
  signInWithGoogle,
  signInWithApple,
} from '../../services/auth.service.js'
import { requestOtp, verifyOtp } from '../../services/otp.service.js'
import { verifyAccessToken } from '../../services/token.service.js'
import { UserType } from '../../constants.js'

const extractDeviceInfo = (request) => ({
  user_agent: request.headers['user-agent'] || null,
  ip: request.ip || null,
})

// Phase 4 §2.1 — Resolve the trusted anonymous customer id for the merge path.
// Source of truth is a verified Bearer JWT. The body field was deliberately
// dropped: a tamper-able UUID in the body lets anyone who learns a victim's
// anon id stamp `account_belongs_to` on it, which would mis-route their
// transactions during reconciliation. The JWT is HS256-signed with
// AUTH_JWT_SECRET — un-forgeable.
//
// Rules:
//   - Bearer valid + customer + customer.is_anonymous → return that customer id.
//   - Bearer valid + customer but NOT anonymous (already verified)         → null
//   - Bearer absent or invalid                                             → null
const resolveAnonymousCustomerId = async ({ bearer }) => {
  if (!bearer || !bearer.startsWith('Bearer ')) return null
  try {
    const claims = verifyAccessToken(bearer.slice('Bearer '.length))
    if (claims.userType !== UserType.CUSTOMER) return null
    const customer = await getCustomerById(claims.userId)
    return customer?.is_anonymous ? customer.id : null
  } catch {
    return null
  }
}

const sendAuthError = (reply, err) => {
  if (!err.statusCode) reply.request.log.error({ err }, 'Unhandled auth error')
  return reply.code(err.statusCode || 500).send({
    success: false,
    error: {
      code: err.code || 'INTERNAL',
      message: err.message,
      ...(err.details && { details: err.details }),
    },
  })
}

export const clientAuthRoutes = async (app) => {
  // --- Phone OTP ---

  app.post('/otp/request', async (request, reply) => {
    const { phone, channel } = request.body || {}
    try {
      const result = await requestOtp({
        phone,
        userType: UserType.CUSTOMER,
        ipAddress: request.ip,
        channel,
      })
      return reply.send({ success: true, data: result })
    } catch (err) {
      return sendAuthError(reply, err)
    }
  })

  app.post('/otp/verify', async (request, reply) => {
    const { otp_request_id, code } = request.body || {}
    try {
      const { phone, user_type } = await verifyOtp({ otpRequestId: otp_request_id, code })
      if (user_type !== UserType.CUSTOMER) {
        return reply.code(400).send({
          success: false,
          error: { code: 'WRONG_FLOW', message: 'This OTP was issued for a different user type' },
        })
      }
      // Phase 4 §2.1 — the anon prefix used to drive the
      // account_belongs_to merge is derived ONLY from the verified Bearer
      // JWT. Clients that need the merge must send their anonymous session's
      // access_token as `Authorization: Bearer …`. The body field is no
      // longer accepted.
      const anonymousCustomerId = await resolveAnonymousCustomerId({
        bearer: request.headers.authorization,
      })
      const { tokens, profile } = await completeCustomerPhoneSignIn({
        phone,
        anonymousCustomerId,
        deviceInfo: extractDeviceInfo(request),
      })
      return reply.send({ success: true, data: { ...tokens, profile } })
    } catch (err) {
      return sendAuthError(reply, err)
    }
  })

  // --- Google ---

  app.post('/google', async (request, reply) => {
    const { id_token } = request.body || {}
    if (!id_token) {
      return reply.code(422).send({
        success: false,
        error: { code: 'VALIDATION_ERROR', message: 'id_token is required' },
      })
    }
    try {
      // Phase 4 §2.1 — anon prefix is derived ONLY from the Bearer JWT;
      // body field `anonymous_customer_id` is not accepted.
      const anonymousCustomerId = await resolveAnonymousCustomerId({
        bearer: request.headers.authorization,
      })
      const { tokens, profile } = await signInWithGoogle({
        idToken: id_token,
        anonymousCustomerId,
        deviceInfo: extractDeviceInfo(request),
      })
      return reply.send({ success: true, data: { ...tokens, profile } })
    } catch (err) {
      return sendAuthError(reply, err)
    }
  })

  // --- Apple ---

  app.post('/apple', async (request, reply) => {
    const { id_token } = request.body || {}
    if (!id_token) {
      return reply.code(422).send({
        success: false,
        error: { code: 'VALIDATION_ERROR', message: 'id_token is required' },
      })
    }
    try {
      // Phase 4 §2.1 — same Bearer-only contract as /otp/verify and /google.
      const anonymousCustomerId = await resolveAnonymousCustomerId({
        bearer: request.headers.authorization,
      })
      const { tokens, profile } = await signInWithApple({
        idToken: id_token,
        anonymousCustomerId,
        deviceInfo: extractDeviceInfo(request),
      })
      return reply.send({ success: true, data: { ...tokens, profile } })
    } catch (err) {
      return sendAuthError(reply, err)
    }
  })

  // --- Current user profile ---

  app.get('/me', { preHandler: authenticate }, async (request, reply) => {
    if (request.auth.userType !== UserType.CUSTOMER) {
      return reply.code(403).send({
        success: false,
        error: { code: 'FORBIDDEN', message: 'Customer account required' },
      })
    }
    const customer = await getCustomerById(request.auth.userId)
    if (!customer) {
      return reply.code(404).send({
        success: false,
        error: { code: 'NOT_FOUND', message: 'Customer account not found' },
      })
    }
    return reply.send({ success: true, data: customer })
  })

  // --- Phase 4: mark USP screen as seen (one-time gate, idempotent) ---

  app.post('/usp-seen', { preHandler: authenticate }, async (request, reply) => {
    if (request.auth.userType !== UserType.CUSTOMER) {
      return reply.code(403).send({
        success: false,
        error: { code: 'FORBIDDEN', message: 'Customer account required' },
      })
    }
    const updated = await markCustomerUspSeen(request.auth.userId)
    if (!updated) {
      return reply.code(404).send({
        success: false,
        error: { code: 'NOT_FOUND', message: 'Customer account not found' },
      })
    }
    return reply.send({ success: true, data: updated })
  })

  // --- Update display name ---

  app.patch('/profile', { preHandler: authenticate }, async (request, reply) => {
    if (request.auth.userType !== UserType.CUSTOMER) {
      return reply.code(403).send({
        success: false,
        error: { code: 'FORBIDDEN', message: 'Customer account required' },
      })
    }
    const { display_name } = request.body || {}
    if (!display_name || typeof display_name !== 'string' || display_name.trim().length === 0) {
      return reply.code(422).send({
        success: false,
        error: { code: 'VALIDATION_ERROR', message: 'display_name is required' },
      })
    }
    const updated = await updateCustomerDisplayName(request.auth.userId, display_name.trim())
    return reply.send({ success: true, data: updated })
  })
}
