import { BadRequestException, Injectable, NotFoundException } from '@nestjs/common';
import { PrismaService } from '../infra/prisma.service';

export const ADDRESS_LABELS = ['HOME', 'WORK', 'OTHER'] as const;
export type AddressLabel = (typeof ADDRESS_LABELS)[number];

interface UpsertAddressCommand {
  userId: string;
  addressId?: string;
  label: string;
  city: string;
  street: string;
  house: string;
  apartment?: string;
  comment?: string;
  latitude?: number;
  longitude?: number;
  fullAddress?: string;
}

@Injectable()
export class AddressesService {
  constructor(private readonly prisma: PrismaService) {}

  private assertLabel(label: string): asserts label is AddressLabel {
    if (!ADDRESS_LABELS.includes(label as AddressLabel)) {
      throw new BadRequestException('Некорректный тип адреса');
    }
  }

  async list(userId: string) {
    const addresses = await this.prisma.address.findMany({
      where: { userId },
      orderBy: [{ label: 'asc' }, { updatedAt: 'desc' }]
    });
    return { addresses: addresses.map((address) => this.toResponse(address)) };
  }

  async upsert(command: UpsertAddressCommand) {
    this.assertLabel(command.label);

    const city = command.city.trim();
    const street = command.street.trim();
    const house = command.house.trim();
    if (!city || !street || !house) {
      throw new BadRequestException('Укажите город, улицу и дом');
    }

    const payload = {
      label: command.label,
      city,
      street,
      house,
      apartment: command.apartment?.trim() || null,
      comment: command.comment?.trim() || null,
      latitude: command.latitude ?? null,
      longitude: command.longitude ?? null,
      fullAddress: command.fullAddress?.trim() || this.buildFullAddress(city, street, house, command.apartment)
    };

    if (command.addressId) {
      const existing = await this.prisma.address.findFirst({
        where: { id: command.addressId, userId: command.userId }
      });
      if (!existing) {
        throw new NotFoundException('Адрес не найден');
      }
      const address = await this.prisma.address.update({
        where: { id: command.addressId },
        data: payload
      });
      return this.toResponse(address);
    }

    if (command.label === 'HOME' || command.label === 'WORK') {
      const existing = await this.prisma.address.findFirst({
        where: { userId: command.userId, label: command.label }
      });
      if (existing) {
        const address = await this.prisma.address.update({
          where: { id: existing.id },
          data: payload
        });
        return this.toResponse(address);
      }
    }

    const address = await this.prisma.address.create({
      data: {
        userId: command.userId,
        ...payload
      }
    });
    return this.toResponse(address);
  }

  async delete(addressId: string, userId: string) {
    const existing = await this.prisma.address.findFirst({
      where: { id: addressId, userId }
    });
    if (!existing) {
      throw new NotFoundException('Адрес не найден');
    }
    await this.prisma.address.delete({ where: { id: addressId } });
    return { count: 1 };
  }

  private buildFullAddress(city: string, street: string, house: string, apartment?: string) {
    const parts = [`г. ${city}`, `${street}, ${house}`];
    if (apartment?.trim()) parts.push(`кв. ${apartment.trim()}`);
    return parts.join(', ');
  }

  private toResponse(address: {
    id: string;
    userId: string;
    label: string;
    city: string;
    street: string;
    house: string;
    apartment: string | null;
    comment: string | null;
    latitude: number | null;
    longitude: number | null;
    fullAddress: string | null;
    createdAt: Date;
    updatedAt: Date;
  }) {
    return {
      id: address.id,
      userId: address.userId,
      label: address.label,
      city: address.city,
      street: address.street,
      house: address.house,
      apartment: address.apartment ?? undefined,
      comment: address.comment ?? undefined,
      latitude: address.latitude ?? undefined,
      longitude: address.longitude ?? undefined,
      fullAddress: address.fullAddress ?? undefined,
      createdAt: address.createdAt.toISOString(),
      updatedAt: address.updatedAt.toISOString()
    };
  }
}
