import { Injectable, NotFoundException, ServiceUnavailableException } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { StoreConfig } from '../../entities/store-config.entity';
import { StoreCategory } from '../../entities/store-category.entity';
import { StoreItem } from '../../entities/store-item.entity';
import { StoreTransaction } from '../../entities/store-transaction.entity';
import { License } from '../../entities/license.entity';
import { UpdateStoreConfigDto } from './dto/update-store-config.dto';
import { CreateCategoryDto } from './dto/create-category.dto';
import { CreateItemDto } from './dto/create-item.dto';
import { PurchaseDto } from './dto/purchase.dto';

@Injectable()
export class WebstoreService {
  constructor(
    @InjectRepository(StoreConfig)
    private readonly configRepo: Repository<StoreConfig>,
    @InjectRepository(StoreCategory)
    private readonly categoryRepo: Repository<StoreCategory>,
    @InjectRepository(StoreItem)
    private readonly itemRepo: Repository<StoreItem>,
    @InjectRepository(StoreTransaction)
    private readonly transactionRepo: Repository<StoreTransaction>,
    @InjectRepository(License)
    private readonly licenseRepo: Repository<License>,
  ) {}

  // Admin Methods
  async getConfig(licenseId: string): Promise<StoreConfig> {
    let config = await this.configRepo.findOne({
      where: { license_id: licenseId },
    });

    if (!config) {
      // Create default config
      config = this.configRepo.create({
        license_id: licenseId,
        store_name: 'My Store',
        enabled: false,
        sandbox_mode: true,
      });
      await this.configRepo.save(config);
    }

    return config;
  }

  async updateConfig(licenseId: string, dto: UpdateStoreConfigDto): Promise<StoreConfig> {
    let config = await this.configRepo.findOne({
      where: { license_id: licenseId },
    });

    if (!config) {
      config = this.configRepo.create({
        license_id: licenseId,
        ...dto,
      });
    } else {
      Object.assign(config, dto);
      config.updated_at = new Date();
    }

    return this.configRepo.save(config);
  }

  async getCategories(licenseId: string): Promise<StoreCategory[]> {
    return this.categoryRepo.find({
      where: { license_id: licenseId },
      order: { display_order: 'ASC', name: 'ASC' },
    });
  }

  async createCategory(licenseId: string, dto: CreateCategoryDto): Promise<StoreCategory> {
    const category = this.categoryRepo.create({
      license_id: licenseId,
      ...dto,
    });
    return this.categoryRepo.save(category);
  }

  async updateCategory(licenseId: string, categoryId: string, dto: Partial<CreateCategoryDto>): Promise<StoreCategory> {
    const category = await this.categoryRepo.findOne({
      where: { id: categoryId, license_id: licenseId },
    });

    if (!category) {
      throw new NotFoundException('Category not found');
    }

    Object.assign(category, dto);
    return this.categoryRepo.save(category);
  }

  async deleteCategory(licenseId: string, categoryId: string): Promise<void> {
    const result = await this.categoryRepo.delete({
      id: categoryId,
      license_id: licenseId,
    });

    if (result.affected === 0) {
      throw new NotFoundException('Category not found');
    }
  }

  async getItems(licenseId: string): Promise<StoreItem[]> {
    return this.itemRepo.find({
      where: { license_id: licenseId },
      relations: ['category'],
      order: { created_at: 'DESC' },
    });
  }

  async createItem(licenseId: string, dto: CreateItemDto): Promise<StoreItem> {
    const item = this.itemRepo.create({
      license_id: licenseId,
      ...dto,
    });
    return this.itemRepo.save(item);
  }

  async updateItem(licenseId: string, itemId: string, dto: Partial<CreateItemDto>): Promise<StoreItem> {
    const item = await this.itemRepo.findOne({
      where: { id: itemId, license_id: licenseId },
    });

    if (!item) {
      throw new NotFoundException('Item not found');
    }

    Object.assign(item, dto);
    item.updated_at = new Date();
    return this.itemRepo.save(item);
  }

  async deleteItem(licenseId: string, itemId: string): Promise<void> {
    const result = await this.itemRepo.delete({
      id: itemId,
      license_id: licenseId,
    });

    if (result.affected === 0) {
      throw new NotFoundException('Item not found');
    }
  }

  async getTransactions(licenseId: string): Promise<StoreTransaction[]> {
    return this.transactionRepo.find({
      where: { license_id: licenseId },
      relations: ['item'],
      order: { created_at: 'DESC' },
    });
  }

  // Public Methods
  async getPublicStore(subdomain: string) {
    const license = await this.licenseRepo.findOne({
      where: { subdomain },
    });

    if (!license) {
      throw new NotFoundException('Store not found');
    }

    const config = await this.configRepo.findOne({
      where: { license_id: license.id },
    });

    if (!config || !config.enabled) {
      throw new NotFoundException('Store not available');
    }

    return {
      store_name: config.store_name,
      description: config.description,
      currency: config.currency,
    };
  }

  async getPublicItems(subdomain: string) {
    const license = await this.licenseRepo.findOne({
      where: { subdomain },
    });

    if (!license) {
      throw new NotFoundException('Store not found');
    }

    const items = await this.itemRepo
      .createQueryBuilder('item')
      .leftJoinAndSelect('item.category', 'category')
      .where('item.license_id = :licenseId', { licenseId: license.id })
      .andWhere('item.enabled = true')
      .andWhere('(category.visible = true OR item.category_id IS NULL)')
      .orderBy('category.display_order', 'ASC')
      .addOrderBy('item.name', 'ASC')
      .getMany();

    return items.map(item => ({
      id: item.id,
      name: item.name,
      description: item.description,
      price: item.price,
      image_url: item.image_url,
      item_type: item.item_type,
      category_name: item.category?.name || null,
    }));
  }

  async createPurchase(subdomain: string, dto: PurchaseDto) {
    const license = await this.licenseRepo.findOne({
      where: { subdomain },
    });

    if (!license) {
      throw new NotFoundException('Store not found');
    }

    const item = await this.itemRepo.findOne({
      where: { id: dto.item_id, license_id: license.id, enabled: true },
    });

    if (!item) {
      throw new NotFoundException('Item not found');
    }

    // Beta: real PayPal/Stripe processing is not wired yet. Refuse honestly
    // instead of writing a pending transaction and handing the player a fake
    // order token that resolves to nowhere. (item lookup above still validates
    // the request so the storefront UI can show the catalogue.)
    void item;
    throw new ServiceUnavailableException(
      'Storefront checkout is not available yet — payment processing is coming soon.',
    );
  }
}
