import { Injectable, HttpException, HttpStatus } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository, In, IsNull, Not } from 'typeorm';
import { Constants } from '../../common/constants';
import { PaymentMethod } from '../../entities/payment-method.entity';
import { Countries } from '../../entities/countries.entity';
import { CountryPaymentMethod } from '../../entities/country-payment-method.entity';
import { InfluencerPaymentMethod } from '../../entities/influencer-payment-method.entity';
import { PaymentMethodRepository } from './payment-method.repository';
import { PaymentMethodCreateDto } from './dtos/payment-method-create.dto';
import { PaymentMethodUpdateDto } from './dtos/payment-method-update.dto';
import { PaymentMethodIdParamDto } from './dtos/payment-method-id-param.dto';
import { PaymentMethodListQueryDto } from './dtos/payment-method-list-query.dto';
import { AssignPaymentMethodDto } from './dtos/assign-payment-method.dto';
import { CountryPaymentMethodListQueryDto } from './dtos/country-payment-method-list-query.dto';
import { CountryIdParamDto } from './dtos/country-id-param.dto';
import { InfluencerPaymentMethodCreateDto } from './dtos/influencer-payment-method-create.dto';
import { responseMessages } from 'src/messages/response-messages';
import { Users } from 'src/entities/users.entity';

@Injectable()
export class PaymentMethodService {
  constructor(
    private paymentMethodRepository: PaymentMethodRepository,
    @InjectRepository(PaymentMethod)
    private paymentMethodRepo: Repository<PaymentMethod>,
    @InjectRepository(Countries)
    private countriesRepo: Repository<Countries>,
    @InjectRepository(CountryPaymentMethod)
    private countryPaymentMethodRepo: Repository<CountryPaymentMethod>,
    @InjectRepository(InfluencerPaymentMethod)
    private influencerPaymentMethodRepo: Repository<InfluencerPaymentMethod>,
    @InjectRepository(Users)
    private Users: Repository<Users>,
  ) { }

  async create(request: PaymentMethodCreateDto) {
    const existing = await this.paymentMethodRepo.findOne({
      where: { name: request.name },
    });
    if (existing) {
      throw new HttpException(responseMessages.en.payment_method.payment_method_already_exists, HttpStatus.BAD_REQUEST);
    }
    const data: Partial<PaymentMethod> = {
      name: request.name,
      is_active: request.is_active !== undefined ? request.is_active : true,
      json_text: request.json_text ?? null,
    };
    const created = await this.paymentMethodRepository.createData(data);
    return { data: created };
  }

  async list(queryParam: PaymentMethodListQueryDto) {
    const page = Math.max(1, Number(queryParam.page) || 1);
    const pageSize = Number(queryParam.per_page) || Constants?.faqs_list || 50;
    const offset = (page - 1) * pageSize;
    const nextOffset = page * pageSize;

    const sortByMap: Record<string, string> = {
      name: 'payment_methods.name',
      is_active: 'payment_methods.is_active',
      created_at: 'payment_methods.created_at',
    };
    const sort_by = sortByMap[queryParam.sort_by || ''] || 'payment_methods.created_at';
    const sort_type = (queryParam.sort || 'ASC').toUpperCase() === 'DESC' ? 'DESC' : 'ASC';

    let qb = this.paymentMethodRepo
      .createQueryBuilder('payment_methods')
      .where('payment_methods.deleted_at IS NULL')
      .orderBy(sort_by, sort_type);

    if (queryParam.search) {
      qb = qb.andWhere('payment_methods.name ILIKE :search', { search: `%${queryParam.search}%` });
    }
    if (queryParam.is_active !== undefined) {
      qb = qb.andWhere('payment_methods.is_active = :is_active', { is_active: queryParam.is_active });
    }

    const [results, total] = await qb.skip(offset).take(pageSize).getManyAndCount();

    return {
      data: results,
      total,
      hasMoreResults: total > nextOffset ? 1 : 0,
    };
  }

  async getById(params: PaymentMethodIdParamDto) {
    const paymentMethod: any = await this.paymentMethodRepo.findOne({
      where: { id: params.payment_method_id },
    });
    if (!paymentMethod) {
      throw new HttpException(responseMessages.en.payment_method.payment_method_not_found, HttpStatus.NOT_FOUND);
    }
    const influencerPaymentMethodsCount = await this.influencerPaymentMethodRepo.count({
      where: { payment_method: { id: params.payment_method_id } },
    });
    paymentMethod.influencerPaymentMethodsCount = influencerPaymentMethodsCount;
    return { data: paymentMethod };
  }

  async update(params: PaymentMethodIdParamDto, request: PaymentMethodUpdateDto) {
    const existing = await this.paymentMethodRepo.findOne({
      where: { id: params.payment_method_id },
    });
    if (!existing) {
      throw new HttpException(responseMessages.en.payment_method.payment_method_not_found, HttpStatus.NOT_FOUND);
    }
    if (existing.name !== request.name) {
      const existingName = await this.paymentMethodRepo.findOne({
        where: { name: request.name, id: Not(params.payment_method_id) },
      });
      if (existingName) {
        throw new HttpException(responseMessages.en.payment_method.payment_method_already_exists, HttpStatus.BAD_REQUEST);
      }
    }
    Object.assign(existing, request)
    await this.paymentMethodRepo.save(existing);
    return { data: existing };
  }

  async delete(params: PaymentMethodIdParamDto) {
    const existing = await this.paymentMethodRepo.findOne({
      relations: ['countryPaymentMethods'],
      where: { id: params.payment_method_id },
    });
    if (!existing) {
      throw new HttpException(responseMessages.en.payment_method.payment_method_not_found, HttpStatus.NOT_FOUND);
    }
    if (existing.countryPaymentMethods.length > 0) {
      throw new HttpException(responseMessages.en.payment_method.payment_method_assigned_to_countries, HttpStatus.BAD_REQUEST);
    }
    await this.paymentMethodRepo.softRemove(existing);
    return;
  }

  async changeStatus(params: PaymentMethodIdParamDto) {
    const existing = await this.paymentMethodRepo.findOne({
      relations: ['countryPaymentMethods'],
      where: { id: params.payment_method_id },
    });
    if (!existing) {
      throw new HttpException(responseMessages.en.payment_method.payment_method_not_found, HttpStatus.NOT_FOUND);
    }
    if (existing.countryPaymentMethods.length > 0) {
      throw new HttpException(responseMessages.en.payment_method.payment_method_assigned_to_countries, HttpStatus.BAD_REQUEST);
    }
    existing.is_active = !existing.is_active;
    await this.paymentMethodRepo.save(existing);
    return { data: existing };
  }

  async assignPaymentMethods(request: AssignPaymentMethodDto) {
    // Check if country exists
    const country = await this.countriesRepo.findOne({
      where: { id: request.country_id },
    });
    if (!country) {
      throw new HttpException('Country not found.', HttpStatus.NOT_FOUND);
    }

    // Check if all payment methods exist
    const paymentMethods = await this.paymentMethodRepo.find({
      where: { id: In(request.payment_method_ids) },
    });
    if (paymentMethods.length !== request.payment_method_ids.length) {
      throw new HttpException(responseMessages.en.payment_method.at_least_one_payment_method_required, HttpStatus.NOT_FOUND);
    }

    // Remove existing assignments for this country
    await this.countryPaymentMethodRepo.delete({ country: { id: request.country_id } });

    // Create new assignments
    const assignments = request.payment_method_ids.map((paymentMethodId) => {
      const assignment = new CountryPaymentMethod();
      assignment.country = country;
      assignment.payment_method = { id: paymentMethodId } as PaymentMethod;
      assignment.setDefaults();
      return assignment;
    });

    await this.countryPaymentMethodRepo.save(assignments);
    return;
  }

  async listCountriesWithPaymentMethods(queryParam: CountryPaymentMethodListQueryDto) {
    const page = Math.max(1, Number(queryParam.page) || 1);
    const pageSize = Number(queryParam.per_page) || Constants?.faqs_list || 50;
    const offset = (page - 1) * pageSize;
    const nextOffset = page * pageSize;

    const sortByMap: Record<string, string> = {
      country_name: 'countries.country_name',
      created_at: 'countries.created_at',
    };
    const sort_by = sortByMap[queryParam.sort_by || ''] || 'countries.country_name';
    const sort_type = (queryParam.sort || 'ASC').toUpperCase() === 'DESC' ? 'DESC' : 'ASC';

    let query = this.countriesRepo
      .createQueryBuilder('countries')
      .leftJoinAndSelect('countries.countryPaymentMethods', 'cpm', 'cpm.deleted_at IS NULL')
      .leftJoinAndSelect('cpm.payment_method', 'payment_method', 'payment_method.deleted_at IS NULL AND payment_method.is_active = true')
      .where('countries.deleted_at IS NULL')
      .select([
        'countries.id',
        'countries.country_name',
        'countries.two_digit_code',
        'countries.three_digit_code',
        'countries.country_code',
        'countries.order_by',
        'cpm.id',
        'cpm.payment_method_id',
        'payment_method.id',
        'payment_method.name',
        'payment_method.is_active',
        'payment_method.json_text',
      ])
      .orderBy(sort_by, sort_type);

    if (queryParam.search) {
      query = query.andWhere(
        '(countries.country_name ILIKE :search OR countries.two_digit_code ILIKE :search OR countries.three_digit_code ILIKE :search)',
        { search: `%${queryParam.search}%` },
      );
    }

    // Filter only countries that have payment methods assigned
    query = query.andWhere('cpm.id IS NOT NULL');

    const [results, total] = await query.skip(offset).take(pageSize).getManyAndCount();


    return {
      data: results,
      total,
      hasMoreResults: total > nextOffset ? 1 : 0,
    };
  }

  async getCountryDetails(params: CountryIdParamDto) {
    const country: any = await this.countriesRepo
      .createQueryBuilder('countries')
      .leftJoinAndSelect('countries.countryPaymentMethods', 'cpm', 'cpm.deleted_at IS NULL')
      .leftJoinAndSelect('cpm.payment_method', 'payment_method', 'payment_method.deleted_at IS NULL AND payment_method.is_active = true')
      .where('countries.id = :countryId', { countryId: params.country_id })
      .andWhere('countries.deleted_at IS NULL')
      .select(['countries.id', 'countries.country_name', 'countries.two_digit_code', 'countries.three_digit_code', 'countries.country_code', 'countries.order_by', 'cpm.id', 'cpm.payment_method_id', 'payment_method.id', 'payment_method.name', 'payment_method.is_active'])
      .getOne();

    if (!country) {
      throw new HttpException('Country not found.', HttpStatus.NOT_FOUND);
    }
    for (const cpm of country.countryPaymentMethods) {
      const influencerPaymentMethodsCount = await this.influencerPaymentMethodRepo.count({
        relations: ['influencer'],
        where: { payment_method: { id: cpm.payment_method.id }, influencer: { country: country.two_digit_code } },
      });
      cpm.influencerPaymentMethodsCount = influencerPaymentMethodsCount;
    }
    console.log(country.countryPaymentMethods);
    return { data: country };
  }

  async getPaymentMethodsByInfluencerCountry(queryParam: any, influencer: any) {
    // Check if user has a country set
    const user = queryParam?.user_id ? await this.Users.findOne({ where: { id: queryParam.user_id } }) : influencer;
    console.log(user);
    if (!user?.country) {
      throw new HttpException('Influencer country not found.', HttpStatus.NOT_FOUND);
    }
    // Find country by two_digit_code matching the influencer's country
    const country = await this.countriesRepo
      .createQueryBuilder('countries')
      .leftJoinAndSelect('countries.countryPaymentMethods', 'cpm', 'cpm.deleted_at IS NULL')
      .leftJoinAndSelect('cpm.payment_method', 'payment_method', 'payment_method.deleted_at IS NULL AND payment_method.is_active = true')
      .where('countries.two_digit_code = :countryCode', { countryCode: user.country })
      .andWhere('countries.deleted_at IS NULL')
      .select([
        'countries.id',
        'countries.country_name',
        'countries.two_digit_code',
        'countries.three_digit_code',
        'countries.country_code',
        'countries.order_by',
        'cpm.id',
        'cpm.payment_method_id',
        'payment_method.id',
        'payment_method.name',
        'payment_method.is_active',
        'payment_method.json_text'
      ])
      .getOne();

    if (!country) {
      throw new HttpException('Country not found for the influencer.', HttpStatus.NOT_FOUND);
    }

    const influencerPaymentMethods = await this.influencerPaymentMethodRepo.find({
      relations: ['payment_method'],
      where: { influencer: { id: user.id } },
    });

    country.countryPaymentMethods.map((cpm) => {
      const influencerPaymentMethod = influencerPaymentMethods.find((ipm) => ipm?.payment_method?.id === cpm?.payment_method?.id);
      cpm['influencerValue'] = null;
      if (influencerPaymentMethod) {
        cpm['influencerValue'] = influencerPaymentMethod.json_data;
      }
    });

    return { data: country };
  }

  async createInfluencerPaymentMethod(user: any, request: InfluencerPaymentMethodCreateDto) {
    // Check if payment method exists and is active
    const paymentMethod = await this.paymentMethodRepo.findOne({
      where: { id: request.payment_method_id },
    });
    if (!paymentMethod) {
      throw new HttpException(responseMessages.en.payment_method.payment_method_not_found, HttpStatus.NOT_FOUND);
    }
    if (!paymentMethod.is_active) {
      throw new HttpException('Payment method is not active.', HttpStatus.BAD_REQUEST);
    }

    // Check if user has a country set
    if (!user.country) {
      throw new HttpException('Influencer country not found.', HttpStatus.NOT_FOUND);
    }

    // Verify that the payment method is available for the influencer's country
    const country = await this.countriesRepo.findOne({
      where: { two_digit_code: user.country },
    });
    if (!country) {
      throw new HttpException('Country not found for the influencer.', HttpStatus.NOT_FOUND);
    }

    const countryPaymentMethod = await this.countryPaymentMethodRepo.findOne({
      where: {
        country: { id: country.id },
        payment_method: { id: request.payment_method_id },
      },
    });
    if (!countryPaymentMethod) {
      throw new HttpException('This payment method is not available for your country.', HttpStatus.BAD_REQUEST);
    }

    // Check if influencer already has this payment method (including soft-deleted)
    const existing = await this.influencerPaymentMethodRepo.findOne({
      where: {
        influencer: { id: user.id },
        payment_method: { id: request.payment_method_id },
      },
      withDeleted: true,
    });

    if (existing) {
      // Update existing payment method
      existing.json_data = request.json_data ?? null;
      existing.is_active = request.is_active !== undefined ? request.is_active : existing.is_active;
      existing.deleted_at = null; // Restore if soft-deleted
      const updated = await this.influencerPaymentMethodRepo.save(existing);
      return { data: updated };
    }

    // Create new payment method assignment
    const influencerPaymentMethod = new InfluencerPaymentMethod();
    influencerPaymentMethod.influencer = user;
    influencerPaymentMethod.payment_method = paymentMethod;
    influencerPaymentMethod.json_data = request.json_data ?? null;
    influencerPaymentMethod.is_active = request.is_active !== undefined ? request.is_active : true;
    influencerPaymentMethod.setDefaults();

    const saved = await this.influencerPaymentMethodRepo.save(influencerPaymentMethod);
    return { data: saved };
  }

  async script() {
    // CREATE BANK Bank Account METHOD & ASSIGN THIS METHOD TO HONGKONG
    const countryPaymentMethods = await this.countryPaymentMethodRepo.find({
      relations: ['payment_method', 'country'],
      where: { deleted_at: IsNull(), id: '08b0311b-b5a9-4c60-b9e6-22b040a3f8dc', payment_method: { is_active: true }, country: { two_digit_code: 'HK' } },
    });
    for (const cpm of countryPaymentMethods) {

      const influencers = await this.Users
        .createQueryBuilder('user')
        .innerJoinAndSelect('user.bankDetails', 'bank')
        .where('user.country = :country', { country: 'HK' })
        .getMany();
      for (const influencer of influencers) {
        const json_text = {
          'new_field': influencer.bankDetails.holder_name ?? "",
          'new_field_1': influencer.bankDetails.account_number ?? "",
          'new_field_2': "",
          'new_field_3': "",
          'new_field_4': influencer.bankDetails.qr_code ?? "",
        }

        const influencerPaymentMethod = {
          influencer: { id: influencer.id },
          payment_method: { id: cpm.payment_method.id },
          json_data: json_text,
          is_active: true,
        }
        await this.influencerPaymentMethodRepo.save(influencerPaymentMethod);
      }
    }
  }
}
