import { Injectable, NotFoundException, ForbiddenException, Logger } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { Module } from '../../entities/module.entity';
import { ModulePurchase } from '../../entities/module-purchase.entity';
import { ModuleInstallation } from '../../entities/module-installation.entity';

@Injectable()
export class StoreService {
  private readonly logger = new Logger(StoreService.name);

  constructor(
    @InjectRepository(Module)
    private readonly moduleRepo: Repository<Module>,
    @InjectRepository(ModulePurchase)
    private readonly purchaseRepo: Repository<ModulePurchase>,
    @InjectRepository(ModuleInstallation)
    private readonly installationRepo: Repository<ModuleInstallation>,
  ) {}

  async getCatalog(): Promise<Module[]> {
    return this.moduleRepo.find({
      order: { created_at: 'DESC' },
    });
  }

  async getMyModules(licenseId: string) {
    const purchases = await this.purchaseRepo.find({
      where: { license_id: licenseId },
      relations: ['module'],
      order: { purchased_at: 'DESC' },
    });

    const installations = await this.installationRepo.find({
      where: { license_id: licenseId },
      relations: ['module'],
    });

    return {
      purchased: purchases,
      installed: installations,
    };
  }

  async purchaseModule(licenseId: string, moduleId: string): Promise<ModulePurchase> {
    // Check if already purchased.
    const existing = await this.purchaseRepo.findOne({
      where: { license_id: licenseId, module_id: moduleId },
    });

    if (existing) {
      return existing;
    }

    const module = await this.moduleRepo.findOne({ where: { id: moduleId } });
    if (!module) {
      throw new NotFoundException('Module not found');
    }

    const purchase = this.purchaseRepo.create({
      license_id: licenseId,
      module_id: moduleId,
      transaction_id: `txn_${Date.now()}`,
      amount_paid: parseFloat(module.price_usd.toString()),
    });

    return this.purchaseRepo.save(purchase);
  }

  async installModule(licenseId: string, moduleId: string): Promise<ModuleInstallation> {
    // Verify purchase exists.
    const purchase = await this.purchaseRepo.findOne({
      where: { license_id: licenseId, module_id: moduleId },
    });

    if (!purchase) {
      throw new ForbiddenException('Module not purchased');
    }

    // Verify module exists.
    const module = await this.moduleRepo.findOne({ where: { id: moduleId } });
    if (!module) {
      throw new NotFoundException('Module not found');
    }

    // Idempotent: return existing installation record if one already exists.
    const existing = await this.installationRepo.findOne({
      where: { license_id: licenseId, module_id: moduleId },
    });

    if (existing) {
      // If previously failed, reset to pending so it can be retried.
      if (existing.status === 'failed') {
        existing.status = 'installed';
        existing.installed_at = new Date();
        existing.error_message = null;
        return this.installationRepo.save(existing);
      }
      return existing;
    }

    // Create installation record and mark as installed.
    // In a full implementation this would trigger an async deployment pipeline.
    const installation = this.installationRepo.create({
      license_id: licenseId,
      module_id: moduleId,
      status: 'installed',
      installed_at: new Date(),
      error_message: null,
    });

    const saved = await this.installationRepo.save(installation);

    this.logger.log(
      `Module installed: ${module.name ?? moduleId} for license ${licenseId} (installation id: ${saved.id})`,
    );

    return saved;
  }
}
