import { BadRequestException, Injectable, NotFoundException } from '@nestjs/common';
import { CreateRestaurantDto } from '../dto/create-restaurant.dto';
import { UpdateRestaurantDto } from '../dto/update-restaurant.dto';
import { UpgradeSubscriptionDto } from '../dto/upgrade-subscription.dto';
import { Restaurant } from '../entities/restaurant.entity';
import { EntityManager } from '@mikro-orm/postgresql';
import { RestRepository } from '../repositories/rest.repository';
import { RestMessage } from 'src/common/enums/message.enum';
import { FindRestaurantsDto } from '../dto/find-restaurants.dto';
import { PaginatedResult } from 'src/common/interfaces/pagination.interface';
import { PlanEnum } from '../interface/plan.interface';
import { Admin } from '../../admin/entities/admin.entity';
import { AdminRole } from '../../admin/entities/adminRole.entity';
import { Role } from '../../roles/entities/role.entity';
import { normalizePhone } from 'src/modules/utils/phone.util';
import slugify from 'slugify';
import { NotificationPreference } from '../../notifications/entities/notification-preference.entity';
import { notificationPreferencesData } from '../../../seeders/data/notification-preferences.data';
import { Order } from '../../orders/entities/order.entity';
import { Coupon } from '../../coupons/entities/coupon.entity';
import { Category } from '../../foods/entities/category.entity';
import { Food } from '../../foods/entities/food.entity';
import { Delivery } from '../../delivery/entities/delivery.entity';
import { PaymentMethod } from '../../payments/entities/payment-method.entity';
import { Pager } from '../../pager/entities/pager.entity';
import { Schedule } from '../entities/schedule.entity';
import { WalletTransaction } from '../../users/entities/wallet-transaction.entity';
import { SmsLog } from '../../notifications/entities/smsLogs.entity';
import { Notification } from '../../notifications/entities/notification.entity';


@Injectable()
export class RestaurantsService {
  constructor(
    private readonly em: EntityManager,
    private readonly restRepository: RestRepository,
  ) { }

  async setupRestuarant(dto: CreateRestaurantDto): Promise<Restaurant> {
    return await this.em.transactional(async (em) => {
      // Generate or validate slug
      const slug = slugify(dto.slug ?? dto.name, {
        lower: true,
        strict: true,
        locale: 'fa'
      });


      const validateSubscriptionId = await em.findOne(Restaurant, { subscriptionId: dto.subscriptionId })
      if (validateSubscriptionId) {
        throw new BadRequestException("Duplicate subscriptionId: " + dto.subscriptionId)
      }

      // Create restaurant
      const restaurant = em.create(Restaurant, {
        name: dto.name,
        slug,
        establishedYear: dto.establishedYear,
        phone: dto.phone,
        isActive: true,
        plan: dto.plan,
        domain: `https://dmenu.danakcorp.com/${slug}`,
        subscriptionId: dto.subscriptionId,
        subscriptionEndDate: dto.subscriptionEndDate,
        subscriptionStartDate: dto.subscriptionStartDate,
      });

      // Find the appropriate role based on plan
      const roleName = dto.plan === PlanEnum.Base ? 'مدیر (پلن پایه)' : 'مدیر ( پلن ویژه)';
      const role = await em.findOne(Role, { name: roleName });
      if (!role) {
        throw new BadRequestException(`Role not found for plan: ${dto.plan}`);
      }

      const normalizedPhone = normalizePhone(dto.phone);
      let admin = await em.findOne(Admin, { phone: normalizedPhone });
      if (!admin) {
        admin = em.create(Admin, {
          phone: normalizedPhone,
          firstName: '[نام]',
          lastName: '[نام خانوادگی]',
        });
      }

      // Create admin role relationship
      const adminRole = em.create(AdminRole, {
        admin,
        role,
        restaurant,
      });

      // Create notification preferences for the restaurant
      const notificationPreferences = notificationPreferencesData.map(preferenceData =>
        em.create(NotificationPreference, {
          restaurant,
          title: preferenceData.title,
          channels: preferenceData.channels,
        })
      );

      // Persist all entities
      em.persist([restaurant, admin, adminRole, ...notificationPreferences]);
      await em.flush();

      return restaurant;
    });
  }

  async findAll(dto: FindRestaurantsDto): Promise<PaginatedResult<Restaurant>> {
    return this.restRepository.findAllPaginated({
      page: dto.page,
      limit: dto.limit,
      search: dto.search,
      isActive: dto.isActive,
      plan: dto.plan,
      orderBy: dto.orderBy,
      order: dto.order,
    });
  }

  async findBySlug(slug: string): Promise<Restaurant> {
    const restaurant = await this.em.findOne(Restaurant, { slug });

    if (!restaurant) {
      throw new NotFoundException(`Restaurant with slug "${slug}" not found`);
    }

    return restaurant;
  }

  async findOneOrFail(id: string): Promise<Restaurant> {
    const restaurant = await this.restRepository.findOne({ id });

    if (!restaurant) {
      throw new NotFoundException(RestMessage.NOT_FOUND);
    }

    return restaurant;
  }

  async findOneBySubscriptionId(subscriptionId: string): Promise<Restaurant> {
    console.log('subscriptionId', subscriptionId)
    const restaurant = await this.restRepository.findOne({ subscriptionId });
    console.log('restaurant', restaurant)
    if (!restaurant) {
      throw new NotFoundException(RestMessage.NOT_FOUND);
    }
    return restaurant;
  }

  async getRestaurantSpecification(slug: string) {
    const restaurant = await this.findBySlug(slug);
    return restaurant;
  }
  // TODO : it must be done inside transaction
  async update(id: string, dto: UpdateRestaurantDto): Promise<Restaurant> {

    const restaurant = await this.restRepository.findOne({ id: id });

    if (!restaurant) {
      throw new NotFoundException(RestMessage.NOT_FOUND);
    }

    if (dto.plan && dto.plan !== restaurant.plan) {
      const isPremium = dto.plan === PlanEnum.Premium
      // find admins and make them premium or base 
      const adminRoles = await this.em.find(AdminRole, {
        restaurant: {
          id
        },
        role: {
          name: isPremium ? 'مدیر (پلن پایه)' : 'مدیر ( پلن ویژه)'
        }
      })
      const newRole = await this.em.findOne(Role, {
        name: isPremium ? 'مدیر ( پلن ویژه)' : 'مدیر (پلن پایه)'
      })
      if (!newRole) {
        throw new BadRequestException("Role Not Found")
      }
      adminRoles.forEach(a => {
        a.role = newRole
      })

      this.em.persistAndFlush(adminRoles)

    }

    this.restRepository.assign(restaurant, dto);

    await this.em.persistAndFlush(restaurant);

    return restaurant;
  }

  async remove(id: string) {
    // Soft delete the restaurant by setting isActive to false
    const restaurant = await this.restRepository.findOne({ id });

    if (!restaurant) {
      throw new NotFoundException(RestMessage.NOT_FOUND);
    }

    restaurant.isActive = false;
    await this.em.persistAndFlush(restaurant);

    return restaurant;
  }

  async upgradeSubscription(subscriptionId: string, dto: UpgradeSubscriptionDto): Promise<Restaurant> {
    const restaurant = await this.restRepository.findOne({ subscriptionId });

    if (!restaurant) {
      throw new NotFoundException(RestMessage.NOT_FOUND);
    }

    const isActive = new Date(dto.subscriptionEndDate) > new Date()

    await this.update(restaurant.id, {
      plan: dto.newPlan,
      subscriptionEndDate: dto.subscriptionEndDate,
      isActive
    })

    return restaurant;
  }

  async hardDeleteRestaurant(id: string): Promise<void> {
    return await this.em.transactional(async (em) => {
      // Find the restaurant first
      const restaurant = await em.findOne(Restaurant, { id });

      if (!restaurant) {
        throw new NotFoundException(RestMessage.NOT_FOUND);
      }

      // Delete orders first (will cascade to order items and payments due to cascade settings)
      await em.nativeDelete(Order, { restaurant: id });

      // Delete coupons
      await em.nativeDelete(Coupon, { restaurant: id });

      // Delete foods (will cascade to reviews, favorites, and inventory due to cascade settings)
      await em.nativeDelete(Food, { restaurant: id });

      // Delete categories (foods should be deleted by cascade, but delete explicitly to be safe)
      await em.nativeDelete(Category, { restaurant: id });

      // Delete deliveries
      await em.nativeDelete(Delivery, { restaurant: id });

      // Delete schedules
      await em.nativeDelete(Schedule, { restId: id });

      // Delete notification preferences
      await em.nativeDelete(NotificationPreference, { restaurant: id });

      // Delete notifications
      await em.nativeDelete(Notification, { restaurant: id });

      // Delete SMS logs
      await em.nativeDelete(SmsLog, { restaurant: id });

      // Delete admin roles for this restaurant
      await em.nativeDelete(AdminRole, { restaurant: id });

      // Delete payment methods
      await em.nativeDelete(PaymentMethod, { restaurant: id });

      // Delete pagers
      await em.nativeDelete(Pager, { restaurant: id });

      // Delete wallet transactions
      await em.nativeDelete(WalletTransaction, { restaurant: id });

      // Finally, delete the restaurant itself
      await em.nativeDelete(Restaurant, { id });
    });
  }

}
