import { BaseCommand, flags } from '@adonisjs/core/ace'
import type { CommandOptions } from '@adonisjs/core/types/ace'
import db from '@adonisjs/lucid/services/db'

import User from '#models/user'
import Organization from '#models/organization'
import Plan from '#models/plan'
import Client from '#models/client'
import Invoice from '#models/invoice'
import ActivityEvent from '#models/activity_event'
import RelanceTask from '#models/relance_task'
import CheckinTask from '#models/checkin_task'
import { provisionDefaultPlans } from '#services/default_plans'
import { seedDemoOrg, wipeOrgInvoicePdfs } from '#database/factories'

/**
 * Peuple l'org d'un user existant avec des données de démo réalistes —
 * pour visualiser dashboard, factures, plans en conditions réelles.
 *
 *   node ace seed:demo --email arthurbarre.js@gmail.com
 *   node ace seed:demo --email ... --reset   # wipe avant
 */
export default class SeedDemo extends BaseCommand {
  static commandName = 'seed:demo'
  static description = "Peuple l'organisation d'un user existant avec des données de démo (clients, factures, activité)"

  static options: CommandOptions = {
    startApp: true,
  }

  @flags.string({
    description: "Email du user dont on peuple l'org",
    required: true,
  })
  declare email: string

  @flags.boolean({
    description:
      "Supprime les clients/factures/activité existants de l'org avant le seed",
    default: false,
  })
  declare reset: boolean

  @flags.string({
    description:
      "Nom à donner à l'organisation (ex. 'Arthur Barré'). Si vide, on garde le nom existant ou fallback.",
  })
  declare orgName?: string

  async run() {
    const email = this.email
    if (!email) {
      this.logger.error('Argument requis : --email <user-email>')
      this.exitCode = 1
      return
    }

    const user = await User.findBy('email', String(email).toLowerCase())
    if (!user) {
      this.logger.error(`User introuvable : ${email}`)
      this.exitCode = 1
      return
    }
    this.logger.info(`User trouvé : ${user.fullName ?? user.email} (${user.id})`)

    if (!user.organizationId) {
      this.logger.error('Le user n\'a pas d\'organization rattachée — flow signup pas terminé ?')
      this.exitCode = 1
      return
    }

    await db.transaction(async (trx) => {
      const org = await Organization.findOrFail(user.organizationId!, { client: trx })

      if (this.reset) {
        this.logger.warning('--reset : suppression des clients/factures/activity existants…')
        // Ordre : enfants d'abord pour respecter les FK
        await CheckinTask.query({ client: trx })
          .whereIn(
            'invoice_id',
            db.from('invoices').where('organization_id', org.id).select('id')
          )
          .delete()
        await RelanceTask.query({ client: trx })
          .whereIn(
            'invoice_id',
            db.from('invoices').where('organization_id', org.id).select('id')
          )
          .delete()
        await ActivityEvent.query({ client: trx }).where('organization_id', org.id).delete()
        await Invoice.query({ client: trx }).where('organization_id', org.id).delete()
        await Client.query({ client: trx }).where('organization_id', org.id).delete()

        // Drop tous les PDFs MinIO de l'org pour repartir d'un drive propre.
        // Hors transaction (drive n'a pas de notion de tx) — si la suite
        // échoue, les PDFs sont déjà partis : pas grave, ils étaient destinés
        // au reset de toute façon.
        const wiped = await wipeOrgInvoicePdfs(org.id)
        if (wiped > 0) {
          this.logger.info(`MinIO : ${wiped} PDFs de facture supprimés.`)
        }
      }

      // Configure l'org : nom (si fourni) + bucket volume mensuel
      const targetName =
        this.orgName ??
        (org.name && org.name.length > 0 ? org.name : `Maison ${user.fullName?.split(' ')[1] ?? 'Démo'}`)
      org.useTransaction(trx)
      org.name = targetName
      if (!org.monthlyVolumeBucket) {
        org.monthlyVolumeBucket = '20-50'
      }
      // Reset rubisCount, on le rechargera en fonction des factures seedées.
      org.rubisCount = 0
      await org.save()
      this.logger.info(`Org configurée : "${org.name}"`)

      // Plans : provision si manquants (idempotent)
      await provisionDefaultPlans(org.id, trx)
      const plans = await Plan.query({ client: trx }).where('organization_id', org.id)
      this.logger.info(`Plans disponibles : ${plans.length} (${plans.map((p) => p.name).join(', ')})`)

      // Seed la data
      const result = await seedDemoOrg({
        organizationId: org.id,
        plans,
        trx,
      })

      // Met à jour le compteur rubis de l'org en fonction du seed
      await trx
        .from('organizations')
        .where('id', org.id)
        .update({ rubis_count: result.rubisEarned })

      this.logger.success(
        `Seed terminé : ${result.clients.length} clients · ${result.invoices.length} factures · ${result.rubisEarned} rubis`
      )

      // Petit récap par statut
      const byStatus: Record<string, number> = {}
      for (const inv of result.invoices) {
        byStatus[inv.status] = (byStatus[inv.status] ?? 0) + 1
      }
      for (const [status, count] of Object.entries(byStatus)) {
        this.logger.info(`  · ${status} : ${count}`)
      }

      // User : signature par défaut si vide (utile pour les previews/tests email)
      if (!user.signature) {
        user.useTransaction(trx)
        user.signature = `Cordialement,\n${user.fullName ?? 'L\'équipe'}\n${org.name}`
        await user.save()
        this.logger.info('Signature email par défaut posée sur le user.')
      }
    })

    this.logger.success('Done.')
  }
}
