import {
  BadRequestException,
  Injectable,
  NotFoundException,
} from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { randomUUID } from 'node:crypto';
import { Brackets, Repository } from 'typeorm';
import { assertAdmin } from '../admin/admin-access';
import { User } from '../users/entities/user.entity';
import { Category, SourceType } from '../third-party/entities/category.entity';
import {
  Course,
  OrderFormField,
  OrderFormFieldType,
} from '../third-party/entities/course.entity';
import { ThirdPartyCatalogService } from '../third-party/third-party-catalog.service';
import type {
  CatalogListQuery,
  CreateCatalogCategoryDto,
  CreateCatalogProductDto,
  FulfillmentType,
  OrderFormFieldDto,
  UpdateCatalogCategoryDto,
  UpdateCatalogProductDto,
  UpdateEnabledDto,
} from './dto/catalog.dto';

const SOURCE_ALL = 'all';
const SELF_OWNED_SOURCE: SourceType = 'self_owned';
const DEFAULT_SELF_FULFILLMENT: FulfillmentType = 'local_only';
const FULFILLMENT_TYPES = new Set<FulfillmentType>([
  'third_party_api',
  'local_only',
  'manual',
]);
const ORDER_FORM_FIELD_TYPES = new Set([
  'text',
  'password',
  'phone',
  'number',
  'textarea',
  'select',
  'checkbox',
]);

@Injectable()
export class CatalogService {
  constructor(
    @InjectRepository(Category)
    private readonly categoriesRepository: Repository<Category>,
    @InjectRepository(Course)
    private readonly productsRepository: Repository<Course>,
    private readonly thirdPartyCatalogService: ThirdPartyCatalogService,
  ) {}

  async listCategories(actor: User, query: CatalogListQuery) {
    assertAdmin(actor);
    const { page, pageSize } = this.getPagination(query);
    const keyword = String(query.keyword || '').trim();
    const sourceType = this.normalizeSourceType(query.sourceType);
    const enabled = this.normalizeEnabled(query.enabled);

    const builder = this.categoriesRepository
      .createQueryBuilder('category')
      .orderBy('category.sortOrder', 'ASC')
      .addOrderBy('category.updatedAt', 'DESC')
      .skip((page - 1) * pageSize)
      .take(pageSize);

    if (sourceType) {
      builder.andWhere('category.sourceType = :sourceType', { sourceType });
    }
    if (typeof enabled === 'boolean') {
      builder.andWhere('category.enabled = :enabled', { enabled });
    }
    if (keyword) {
      builder.andWhere(
        new Brackets((qb) => {
          qb.where('category.name LIKE :keyword', {
            keyword: `%${keyword}%`,
          })
            .orWhere('category.externalId LIKE :keyword', {
              keyword: `%${keyword}%`,
            })
            .orWhere('category.remoteCategoryId LIKE :keyword', {
              keyword: `%${keyword}%`,
            });
        }),
      );
    }

    const [list, total] = await builder.getManyAndCount();
    return { list, total, page, pageSize };
  }

  async createCategory(actor: User, dto: CreateCatalogCategoryDto) {
    assertAdmin(actor);
    const name = this.normalizeRequiredString(dto.name, '分类名称不能为空');
    const category = this.categoriesRepository.create({
      id: randomUUID(),
      sourceType: SELF_OWNED_SOURCE,
      apiAccountId: null,
      provider: null,
      externalId: null,
      remoteCategoryId: null,
      name,
      sortOrder: this.normalizeInteger(dto.sortOrder, 0),
      enabled: dto.enabled ?? true,
      rawPayload: null,
      lastSyncedAt: null,
    });

    return this.categoriesRepository.save(category);
  }

  async updateCategory(
    actor: User,
    id: string,
    dto: UpdateCatalogCategoryDto,
  ) {
    assertAdmin(actor);
    const category = await this.getCategory(id);
    this.assertSelfOwned(category, '第三方分类只能同步或停用，不能手工编辑');

    if (dto.name !== undefined) {
      category.name = this.normalizeRequiredString(dto.name, '分类名称不能为空');
    }
    if (dto.sortOrder !== undefined) {
      category.sortOrder = this.normalizeInteger(dto.sortOrder, 0);
    }
    if (typeof dto.enabled === 'boolean') {
      category.enabled = dto.enabled;
    }

    return this.categoriesRepository.save(category);
  }

  async updateCategoryEnabled(actor: User, id: string, dto: UpdateEnabledDto) {
    assertAdmin(actor);
    const category = await this.getCategory(id);
    category.enabled = Boolean(dto.enabled);
    return this.categoriesRepository.save(category);
  }

  async deleteCategory(actor: User, id: string) {
    assertAdmin(actor);
    const category = await this.getCategory(id);
    this.assertSelfOwned(category, '第三方分类不能删除，请使用停用');

    const productCount = await this.productsRepository.count({
      where: { categoryId: category.id },
    });
    if (productCount > 0) {
      throw new BadRequestException('分类下已有商品，不能删除');
    }

    await this.categoriesRepository.remove(category);
    return { id, deleted: true };
  }

  async listProducts(actor: User, query: CatalogListQuery) {
    assertAdmin(actor);
    const { page, pageSize } = this.getPagination(query);
    const keyword = String(query.keyword || '').trim();
    const sourceType = this.normalizeSourceType(query.sourceType);
    const enabled = this.normalizeEnabled(query.enabled);
    const categoryId = String(query.categoryId || '').trim();
    const remoteCategoryId = String(query.remoteCategoryId || '').trim();

    const builder = this.productsRepository
      .createQueryBuilder('product')
      .orderBy('product.sortOrder', 'ASC')
      .addOrderBy('product.updatedAt', 'DESC')
      .skip((page - 1) * pageSize)
      .take(pageSize);

    if (sourceType) {
      builder.andWhere('product.sourceType = :sourceType', { sourceType });
    }
    if (typeof enabled === 'boolean') {
      builder.andWhere('product.enabled = :enabled', { enabled });
    }
    if (categoryId) {
      builder.andWhere('product.categoryId = :categoryId', { categoryId });
    }
    if (remoteCategoryId) {
      builder.andWhere('product.remoteCategoryId = :remoteCategoryId', {
        remoteCategoryId,
      });
    }
    if (keyword) {
      builder.andWhere(
        new Brackets((qb) => {
          qb.where('product.name LIKE :keyword', {
            keyword: `%${keyword}%`,
          })
            .orWhere('product.externalId LIKE :keyword', {
              keyword: `%${keyword}%`,
            })
            .orWhere('product.remoteCourseId LIKE :keyword', {
              keyword: `%${keyword}%`,
            });
        }),
      );
    }

    const [products, total] = await builder.getManyAndCount();
    const categoryMap = await this.getCategoryMap(products);
    return {
      list: products.map((product) => this.toProductItem(product, categoryMap)),
      total,
      page,
      pageSize,
    };
  }

  async createProduct(actor: User, dto: CreateCatalogProductDto) {
    assertAdmin(actor);
    const category = await this.getOptionalCategory(dto.categoryId);
    if (category) {
      this.assertSelfOwned(category, '自营商品只能选择自营分类');
    }

    const product = this.productsRepository.create({
      id: randomUUID(),
      sourceType: SELF_OWNED_SOURCE,
      apiAccountId: null,
      provider: null,
      externalId: null,
      categoryId: category?.id ?? null,
      remoteCategoryId: null,
      remoteCourseId: null,
      name: this.normalizeRequiredString(dto.name, '商品名称不能为空'),
      price: this.normalizePrice(dto.price),
      coverUrl: this.normalizeOptionalString(dto.coverUrl),
      content: this.normalizeOptionalString(dto.content),
      stock: this.normalizeNullableInteger(dto.stock),
      salesLimit: this.normalizeNullableInteger(dto.salesLimit),
      fulfillmentType: this.normalizeFulfillmentType(
        dto.fulfillmentType,
        DEFAULT_SELF_FULFILLMENT,
      ),
      afterSalesNote: this.normalizeOptionalString(dto.afterSalesNote),
      orderFormSchema: this.normalizeOrderFormSchema(dto.orderFormSchema),
      sortOrder: this.normalizeInteger(dto.sortOrder, 0),
      isFavorite: false,
      enabled: dto.enabled ?? true,
      rawPayload: null,
      lastSyncedAt: null,
    });

    const saved = await this.productsRepository.save(product);
    return this.toProductItem(saved, category ? new Map([[category.id, category]]) : new Map());
  }

  async updateProduct(
    actor: User,
    id: string,
    dto: UpdateCatalogProductDto,
  ) {
    assertAdmin(actor);
    const product = await this.getProduct(id);
    this.assertSelfOwned(product, '第三方商品只能同步或停用，不能手工编辑');

    if (dto.categoryId !== undefined) {
      const category = await this.getOptionalCategory(dto.categoryId);
      if (category) {
        this.assertSelfOwned(category, '自营商品只能选择自营分类');
      }
      product.categoryId = category?.id ?? null;
    }
    if (dto.name !== undefined) {
      product.name = this.normalizeRequiredString(dto.name, '商品名称不能为空');
    }
    if (dto.price !== undefined) {
      product.price = this.normalizePrice(dto.price);
    }
    if (dto.coverUrl !== undefined) {
      product.coverUrl = this.normalizeOptionalString(dto.coverUrl);
    }
    if (dto.content !== undefined) {
      product.content = this.normalizeOptionalString(dto.content);
    }
    if (dto.stock !== undefined) {
      product.stock = this.normalizeNullableInteger(dto.stock);
    }
    if (dto.salesLimit !== undefined) {
      product.salesLimit = this.normalizeNullableInteger(dto.salesLimit);
    }
    if (dto.fulfillmentType !== undefined) {
      product.fulfillmentType = this.normalizeFulfillmentType(
        dto.fulfillmentType,
        DEFAULT_SELF_FULFILLMENT,
      );
    }
    if (dto.afterSalesNote !== undefined) {
      product.afterSalesNote = this.normalizeOptionalString(dto.afterSalesNote);
    }
    if (dto.orderFormSchema !== undefined) {
      product.orderFormSchema = this.normalizeOrderFormSchema(dto.orderFormSchema);
    }
    if (dto.sortOrder !== undefined) {
      product.sortOrder = this.normalizeInteger(dto.sortOrder, 0);
    }
    if (typeof dto.enabled === 'boolean') {
      product.enabled = dto.enabled;
    }

    const saved = await this.productsRepository.save(product);
    const categoryMap = await this.getCategoryMap([saved]);
    return this.toProductItem(saved, categoryMap);
  }

  async updateProductEnabled(actor: User, id: string, dto: UpdateEnabledDto) {
    assertAdmin(actor);
    const product = await this.getProduct(id);
    product.enabled = Boolean(dto.enabled);
    const saved = await this.productsRepository.save(product);
    const categoryMap = await this.getCategoryMap([saved]);
    return this.toProductItem(saved, categoryMap);
  }

  async deleteProduct(actor: User, id: string) {
    assertAdmin(actor);
    const product = await this.getProduct(id);
    await this.productsRepository.remove(product);
    return { id, deleted: true };
  }

  async syncThirdPartyCategories(actor: User) {
    assertAdmin(actor);
    return this.thirdPartyCatalogService.syncThirdPartyCatalog({
      triggerType: 'manual',
    });
  }

  async syncThirdPartyProducts(actor: User, query: CatalogListQuery) {
    assertAdmin(actor);
    const remoteCategoryId = this.normalizeOptionalString(
      query.remoteCategoryId,
    );
    return this.thirdPartyCatalogService.syncThirdPartyCatalog({
      triggerType: 'manual',
      remoteCategoryId,
    });
  }

  async listSyncJobs(actor: User, query: CatalogListQuery) {
    assertAdmin(actor);
    return this.thirdPartyCatalogService.listSyncJobs(query);
  }

  private getPagination(query: CatalogListQuery) {
    return {
      page: Math.max(Number(query.page || 1), 1),
      pageSize: Math.min(Math.max(Number(query.pageSize || 10), 1), 100),
    };
  }

  private normalizeSourceType(sourceType?: SourceType | 'all') {
    if (!sourceType || sourceType === SOURCE_ALL) {
      return null;
    }
    if (sourceType === 'third_party' || sourceType === SELF_OWNED_SOURCE) {
      return sourceType;
    }
    throw new BadRequestException('商品来源不正确');
  }

  private normalizeEnabled(value?: string) {
    if (value === undefined || value === '' || value === SOURCE_ALL) {
      return null;
    }
    if (value === 'true' || value === '1') {
      return true;
    }
    if (value === 'false' || value === '0') {
      return false;
    }
    throw new BadRequestException('启停状态不正确');
  }

  private normalizeRequiredString(value: unknown, message: string) {
    const normalized = String(value ?? '').trim();
    if (!normalized) {
      throw new BadRequestException(message);
    }
    return normalized;
  }

  private normalizeOptionalString(value: unknown) {
    const normalized = String(value ?? '').trim();
    return normalized || null;
  }

  private normalizeInteger(value: unknown, fallback: number) {
    if (value === undefined || value === null || value === '') {
      return fallback;
    }
    const numberValue = Number(value);
    if (!Number.isInteger(numberValue)) {
      throw new BadRequestException('排序必须是整数');
    }
    return numberValue;
  }

  private normalizeNullableInteger(value: unknown) {
    if (value === undefined || value === null || value === '') {
      return null;
    }
    const numberValue = Number(value);
    if (!Number.isInteger(numberValue) || numberValue < 0) {
      throw new BadRequestException('库存和限购必须是非负整数');
    }
    return numberValue;
  }

  private normalizePrice(value: unknown) {
    const numberValue = Number(value ?? 0);
    if (!Number.isFinite(numberValue) || numberValue < 0) {
      throw new BadRequestException('商品价格必须是非负数字');
    }
    return numberValue.toFixed(2);
  }

  private normalizeFulfillmentType(
    value: unknown,
    fallback: FulfillmentType,
  ) {
    const normalized = String(value || fallback) as FulfillmentType;
    if (!FULFILLMENT_TYPES.has(normalized)) {
      throw new BadRequestException('交付类型不正确');
    }
    return normalized;
  }

  private normalizeOrderFormSchema(value: unknown): OrderFormField[] | null {
    if (value === undefined || value === null) {
      return null;
    }
    if (!Array.isArray(value)) {
      throw new BadRequestException('下单表单配置必须是数组');
    }
    if (value.length > 30) {
      throw new BadRequestException('下单表单字段不能超过 30 个');
    }

    const keys = new Set<string>();
    return value.map((field, index) => {
      if (!field || typeof field !== 'object' || Array.isArray(field)) {
        throw new BadRequestException('下单表单字段格式不正确');
      }
      const item = field as OrderFormFieldDto;
      const key = this.normalizeFieldKey(item.key, index);
      if (keys.has(key)) {
        throw new BadRequestException(`下单字段 ${key} 重复`);
      }
      keys.add(key);

      const label = this.normalizeRequiredString(
        item.label,
        '下单字段名称不能为空',
      );
      const type = String(item.type || 'text') as OrderFormFieldType;
      if (!ORDER_FORM_FIELD_TYPES.has(type)) {
        throw new BadRequestException(`下单字段 ${label} 类型不正确`);
      }
      const options =
        type === 'select'
          ? this.normalizeFieldOptions(item.options, label)
          : undefined;

      const normalizedField: OrderFormField = {
        key,
        label,
        type,
        required: Boolean(item.required),
        placeholder: this.normalizeOptionalString(item.placeholder),
        ...(options ? { options } : {}),
      };
      return normalizedField;
    });
  }

  private normalizeFieldKey(value: unknown, index: number) {
    const normalized = String(value || `field_${index + 1}`)
      .trim()
      .replace(/\s+/g, '_');
    if (!/^[a-zA-Z][a-zA-Z0-9_]{0,49}$/.test(normalized)) {
      throw new BadRequestException(
        '下单字段标识只能使用字母、数字、下划线，并且必须以字母开头',
      );
    }
    return normalized;
  }

  private normalizeFieldOptions(
    value: OrderFormFieldDto['options'],
    fieldLabel: string,
  ) {
    if (!Array.isArray(value) || !value.length) {
      throw new BadRequestException(`下拉字段 ${fieldLabel} 至少需要一个选项`);
    }
    return value.map((option) => {
      const label = this.normalizeRequiredString(
        option?.label,
        `下拉字段 ${fieldLabel} 的选项名称不能为空`,
      );
      const optionValue = this.normalizeRequiredString(
        option?.value,
        `下拉字段 ${fieldLabel} 的选项值不能为空`,
      );
      return { label, value: optionValue };
    });
  }

  private async getCategory(id: string) {
    const category = await this.categoriesRepository.findOne({ where: { id } });
    if (!category) {
      throw new NotFoundException('分类不存在');
    }
    return category;
  }

  private async getOptionalCategory(id?: string | null) {
    const categoryId = String(id || '').trim();
    if (!categoryId) {
      return null;
    }
    return this.getCategory(categoryId);
  }

  private async getProduct(id: string) {
    const product = await this.productsRepository.findOne({ where: { id } });
    if (!product) {
      throw new NotFoundException('商品不存在');
    }
    return product;
  }

  private assertSelfOwned(
    entity: Category | Course,
    message: string,
  ) {
    if (entity.sourceType !== SELF_OWNED_SOURCE) {
      throw new BadRequestException(message);
    }
  }

  private async getCategoryMap(products: Course[]) {
    const categoryIds = Array.from(
      new Set(products.map((product) => product.categoryId).filter(Boolean)),
    ) as string[];
    if (!categoryIds.length) {
      return new Map<string, Category>();
    }

    const categories = await this.categoriesRepository
      .createQueryBuilder('category')
      .where('category.id IN (:...categoryIds)', { categoryIds })
      .getMany();

    return new Map(categories.map((category) => [category.id, category]));
  }

  private toProductItem(
    product: Course,
    categoryMap: Map<string, Category>,
  ) {
    const category = product.categoryId
      ? categoryMap.get(product.categoryId)
      : null;

    return {
      ...product,
      categoryName: category?.name ?? null,
      categorySourceType: category?.sourceType ?? null,
    };
  }
}
