/**
 * Stripe Routes
 * Payment and subscription management
 */

import express from 'express'
import Stripe from 'stripe'
import { Client, Users } from 'node-appwrite'
import { asyncHandler, ValidationError, NotFoundError } from '../middleware/errorHandler.mjs'
import { validate, rules } from '../middleware/validate.mjs'
import { limiters } from '../middleware/rateLimit.mjs'
import { respond } from '../utils/response.mjs'
import { subscriptions, submissions } from '../services/database.mjs'
import { config } from '../config/index.mjs'
import { log } from '../middleware/logger.mjs'
import { requireAuth } from '../middleware/auth.mjs'
import { loadEmailTemplate, renderTemplate, sendPlainEmail } from '../utils/mailer.mjs'

const router = express.Router()

async function resolveUserEmail(userId, stripeCustomerId) {
  if (userId) {
    try {
      const c = new Client()
        .setEndpoint(config.appwrite.endpoint)
        .setProject(config.appwrite.projectId)
        .setKey(config.appwrite.apiKey)
      const u = await new Users(c).get(userId)
      if (u.email) return u.email
    } catch (e) {
      log.warn('Appwrite Users.get failed', { userId, error: e.message })
    }
  }
  if (stripeCustomerId) {
    try {
      const cust = await stripe.customers.retrieve(String(stripeCustomerId))
      if (cust && !cust.deleted && cust.email) return cust.email
    } catch (e) {
      log.warn('Stripe customer retrieve failed', { error: e.message })
    }
  }
  return null
}
const stripe = new Stripe(config.stripe.secretKey)

function requireAuthUnlessStripeWebhook(req, res, next) {
  if (req.path === '/webhook' && req.method === 'POST') {
    return next()
  }
  return requireAuth(req, res, next)
}

router.use(requireAuthUnlessStripeWebhook)

/**
 * Plan configuration
 */
const PLAN_DISPLAY_NAMES = {
  basic: 'Basic',
  pro: 'Pro',
  business: 'Business',
  free: 'Free',
}

const PLANS = {
  basic: {
    name: 'Basic',
    priceId: config.stripe.prices.basic,
    features: {
      emailAccounts: 1,
      emailsPerDay: 500,
      historicalSync: false,
      customRules: false,
      prioritySupport: false,
    },
  },
  pro: {
    name: 'Pro',
    priceId: config.stripe.prices.pro,
    features: {
      emailAccounts: 3,
      emailsPerDay: -1,
      historicalSync: true,
      customRules: true,
      prioritySupport: false,
    },
  },
  business: {
    name: 'Business',
    priceId: config.stripe.prices.business,
    features: {
      emailAccounts: 10,
      emailsPerDay: -1,
      historicalSync: true,
      customRules: true,
      prioritySupport: true,
    },
  },
}

/**
 * POST /api/subscription/checkout
 * Create subscription checkout session
 */
router.post('/checkout',
  limiters.auth,
  validate({
    body: {
      plan: [rules.required('plan'), rules.isIn('plan', ['basic', 'pro', 'business'])],
    },
  }),
  asyncHandler(async (req, res) => {
    const userId = req.appwriteUser.id
    const { plan, email } = req.body

    const planConfig = PLANS[plan]
    if (!planConfig) {
      throw new ValidationError('Ungültiger Plan', { plan: ['Ungültig'] })
    }

    // Check for existing subscription
    const existing = await subscriptions.getByUser(userId, req.appwriteUser?.email)
    let customerId = existing?.stripeCustomerId

    // Create checkout session
    const sessionConfig = {
      mode: 'subscription',
      payment_method_types: ['card'],
      line_items: [
        {
          price: planConfig.priceId,
          quantity: 1,
        },
      ],
      success_url: `${config.frontendUrl}/setup?subscription=success&setup=auto`,
      cancel_url: `${config.frontendUrl}/pricing?subscription=cancelled`,
      metadata: {
        userId,
        plan,
      },
      subscription_data: {
        trial_period_days: 14,
        metadata: { userId, plan },
      },
      allow_promotion_codes: true,
    }

    if (customerId) {
      sessionConfig.customer = customerId
    } else if (email) {
      sessionConfig.customer_email = email
    }

    const session = await stripe.checkout.sessions.create(sessionConfig)

    log.info(`Checkout Session erstellt für User ${userId}`, { plan })

    respond.success(res, {
      url: session.url,
      sessionId: session.id,
    })
  })
)

/**
 * GET /api/subscription/status
 * Get user's subscription status
 */
router.get('/status', asyncHandler(async (req, res) => {
  const userId = req.appwriteUser.id

  const sub = await subscriptions.getByUser(userId, req.appwriteUser?.email)
  const topKey = config.topSubscriptionPlan
  const plan = sub.plan || topKey
  const features =
    PLANS[plan]?.features ||
    PLANS[topKey]?.features ||
    PLANS.business.features

  respond.success(res, {
    status: sub.status || 'active',
    plan,
    planDisplayName: PLAN_DISPLAY_NAMES[plan] || PLAN_DISPLAY_NAMES[topKey] || 'Business',
    isFreeTier: Boolean(sub.isFreeTier),
    emailsUsedThisMonth: sub.emailsUsedThisMonth ?? 0,
    emailsLimit: sub.emailsLimit ?? -1,
    features,
    currentPeriodEnd: sub.currentPeriodEnd,
    cancelAtPeriodEnd: Boolean(sub.cancelAtPeriodEnd),
  })
}))

/**
 * POST /api/subscription/portal
 * Create Stripe Customer Portal session
 */
router.post('/portal',
  asyncHandler(async (req, res) => {
    const userId = req.appwriteUser.id

    const sub = await subscriptions.getByUser(userId, req.appwriteUser?.email)

    if (!sub?.stripeCustomerId) {
      throw new NotFoundError('Subscription')
    }

    const session = await stripe.billingPortal.sessions.create({
      customer: sub.stripeCustomerId,
      return_url: `${config.frontendUrl}/settings`,
    })

    respond.success(res, { url: session.url })
  })
)

/**
 * POST /api/subscription/cancel
 * Cancel subscription at period end
 */
router.post('/cancel',
  asyncHandler(async (req, res) => {
    const userId = req.appwriteUser.id

    const sub = await subscriptions.getByUser(userId, req.appwriteUser?.email)

    if (!sub?.stripeSubscriptionId) {
      throw new NotFoundError('Subscription')
    }

    await stripe.subscriptions.update(sub.stripeSubscriptionId, {
      cancel_at_period_end: true,
    })

    await subscriptions.update(sub.$id, { cancelAtPeriodEnd: true })

    log.info(`Subscription gekündigt für User ${userId}`)

    respond.success(res, null, 'Subscription wird zum Ende der Periode gekündigt')
  })
)

/**
 * POST /api/subscription/reactivate
 * Reactivate cancelled subscription
 */
router.post('/reactivate',
  asyncHandler(async (req, res) => {
    const userId = req.appwriteUser.id

    const sub = await subscriptions.getByUser(userId, req.appwriteUser?.email)

    if (!sub?.stripeSubscriptionId) {
      throw new NotFoundError('Subscription')
    }

    await stripe.subscriptions.update(sub.stripeSubscriptionId, {
      cancel_at_period_end: false,
    })

    await subscriptions.update(sub.$id, { cancelAtPeriodEnd: false })

    log.info(`Subscription reaktiviert für User ${userId}`)

    respond.success(res, null, 'Subscription wurde reaktiviert')
  })
)

/**
 * POST /stripe/webhook & POST /api/subscription/webhook
 * Stripe webhook handler
 */
router.post('/webhook', express.raw({ type: 'application/json' }), asyncHandler(async (req, res) => {
  const sig = req.headers['stripe-signature']

  let event
  try {
    event = stripe.webhooks.constructEvent(
      req.body,
      sig,
      config.stripe.webhookSecret
    )
  } catch (err) {
    log.error('Webhook Signatur Fehler', { error: err.message })
    return res.status(400).send(`Webhook Error: ${err.message}`)
  }

  log.info(`Stripe Webhook: ${event.type}`)

  try {
    switch (event.type) {
      case 'checkout.session.completed': {
        const session = event.data.object
        const { userId, plan } = session.metadata || {}

        if (userId && session.subscription) {
          const subscription = await stripe.subscriptions.retrieve(session.subscription)

          await subscriptions.upsertByUser(userId, {
            stripeCustomerId: session.customer,
            stripeSubscriptionId: session.subscription,
            plan: plan || 'basic',
            status: subscription.status,
            currentPeriodEnd: new Date(subscription.current_period_end * 1000).toISOString(),
            cancelAtPeriodEnd: subscription.cancel_at_period_end,
          })

          log.success(`Subscription erstellt für User ${userId}`, { plan })
        }

        // Handle one-time payment (legacy)
        if (session.metadata?.submissionId) {
          await submissions.updateStatus(session.metadata.submissionId, 'paid')
          log.success(`Zahlung abgeschlossen: ${session.metadata.submissionId}`)
        }
        break
      }

      case 'customer.subscription.updated': {
        const subscription = event.data.object
        const sub = await subscriptions.getByStripeId(subscription.id)

        if (sub) {
          await subscriptions.update(sub.$id, {
            status: subscription.status,
            currentPeriodEnd: new Date(subscription.current_period_end * 1000).toISOString(),
            cancelAtPeriodEnd: subscription.cancel_at_period_end,
          })

          log.info(`Subscription aktualisiert: ${subscription.id}`)

          try {
            const to = await resolveUserEmail(sub.userId, subscription.customer)
            if (to) {
              const plan = subscription.metadata?.plan || sub.plan || 'current'
              const periodEnd = subscription.current_period_end
                ? new Date(subscription.current_period_end * 1000).toISOString()
                : ''
              const tpl = loadEmailTemplate('subscription-updated')
              const text = renderTemplate(tpl, {
                plan: String(plan),
                status: String(subscription.status || ''),
                periodEndLine: periodEnd ? `Current period ends: ${periodEnd}` : '',
              })
              await sendPlainEmail({
                to,
                subject: 'MailFlow — Subscription updated',
                text,
              })
            }
          } catch (e) {
            log.warn('subscription.updated email skipped', { error: e.message })
          }
        }
        break
      }

      case 'customer.subscription.deleted': {
        const subscription = event.data.object
        const sub = await subscriptions.getByStripeId(subscription.id)

        if (sub) {
          await subscriptions.update(sub.$id, {
            status: 'cancelled',
          })

          log.info(`Subscription gelöscht: ${subscription.id}`)

          try {
            const to = await resolveUserEmail(sub.userId, subscription.customer)
            if (to) {
              const tpl = loadEmailTemplate('subscription-ended')
              const text = renderTemplate(tpl, {
                endedDate: new Date().toISOString(),
              })
              await sendPlainEmail({
                to,
                subject: 'MailFlow — Your subscription has ended',
                text,
              })
            }
          } catch (e) {
            log.warn('subscription.deleted email skipped', { error: e.message })
          }
        }
        break
      }

      case 'invoice.payment_failed': {
        const invoice = event.data.object
        log.warn(`Zahlung fehlgeschlagen: ${invoice.id}`, {
          customer: invoice.customer,
        })
        try {
          let metaUserId
          if (invoice.subscription) {
            const subStripe = await stripe.subscriptions.retrieve(invoice.subscription)
            metaUserId = subStripe.metadata?.userId
          }
          const to = await resolveUserEmail(metaUserId, invoice.customer)
          if (to) {
            const tpl = loadEmailTemplate('payment-failed')
            const text = renderTemplate(tpl, {
              invoiceId: String(invoice.id || ''),
            })
            await sendPlainEmail({
              to,
              subject: 'MailFlow — Payment failed, please update billing',
              text,
            })
          }
        } catch (e) {
          log.warn('invoice.payment_failed email skipped', { error: e.message })
        }
        break
      }

      case 'invoice.payment_succeeded': {
        const invoice = event.data.object
        log.success(`Zahlung erfolgreich: ${invoice.id}`)
        break
      }

      default:
        log.debug(`Unbehandelter Webhook: ${event.type}`)
    }

    res.json({ received: true })
  } catch (err) {
    log.error('Webhook Handler Fehler', { error: err.message, event: event.type })
    res.status(500).json({ error: 'Webhook handler failed' })
  }
}))

export default router
