import { Transaction } from 'sequelize';
import { Sequelize } from 'sequelize-typescript';
import {
  CustomerBaseModel,
  CustomerIndividualModel,
  CustomerBusinessModel,
  BusinessContactModel,
  ObjectAddressModel,
} from '../../models';
import cuid from '../../helpers/cuid';
import { ISyncPayload } from '../../types/sync-payload.type';
import { IBusinessSyncPayload, IIndividualSyncPayload } from '../../interfaces';
import { CustomerStatusEnum, CustomerTypeEnum } from '../../enum';

export class CustomerBaseWriter {
  constructor(private readonly sequelize: Sequelize) {}

  async write(
    payload: ISyncPayload,
    dbTransaction?: Transaction,
  ): Promise<void> {
    let transaction = dbTransaction;
    const createdTransaction = !transaction;
    const ctx = {
      CustomerId: payload?.CustomerId,
      Type: payload?.Type,
      SourceSystem: payload?.SourceSystem,
    };

    try {
      if (!transaction) transaction = await this.sequelize.transaction();

      if (!payload.CustomerId) throw new Error('CustomerId is required.');
      if (!payload.Type)
        throw new Error('Type is required (Individual or Business).');

      // Customer Base
      await CustomerBaseModel.upsert(
        {
          CustomerId: payload.CustomerId,
          Type: payload.Type,
          Email: payload.Email,
          ContactNo: payload.ContactNo,
          Status: CustomerStatusEnum.Active,
          CreatedByUserId: payload.UserId,
          CreatedByCustomerId: !payload.UserId ? payload.CustomerId : null,
          CreatedAt: new Date(),
          UpdatedByUserId: payload.UserId,
          UpdatedByCustomerId: !payload.UserId ? payload.CustomerId : null,
          UpdatedAt: new Date(),
          UpdatedBySystemCode: payload.SourceSystem,
        },
        { transaction },
      );

      // Type-specific
      if (payload.Type === CustomerTypeEnum.Individual) {
        const p = payload as IIndividualSyncPayload;
        if (!p.FullName) throw new Error('FullName is required.');
        if (!p.IdType) throw new Error('IdType is required.');
        if (!p.IdNo) throw new Error('IdNo is required.');

        await CustomerIndividualModel.upsert(
          {
            CustomerId: p.CustomerId,
            FullName: p.FullName,
            IdType: p.IdType,
            IdNo: p.IdNo,
            Title: p.Title ?? 'N/A',
            PreferredName: p.PreferredName ?? p.FullName,
            Birthdate: p.Birthdate ?? undefined,
            Gender: p.Gender ?? undefined,
            Ethnicity: p.Ethnicity ?? undefined,
            Nationality: p.Nationality ?? undefined,
            PreferredLanguage: p.PreferredLanguage ?? undefined,
          },
          { transaction },
        );
      } else if (payload.Type === CustomerTypeEnum.Business) {
        const p = payload as IBusinessSyncPayload;
        if (!p.CompanyName) throw new Error('Company Name is required.');

        await CustomerBusinessModel.upsert(
          {
            CustomerId: p.CustomerId,
            CompanyName: p.CompanyName,
            RegistrationNo: p.RegistrationNo,
            TaxIdentificationNo: p.TaxIdentificationNo,
          },
          { transaction },
        );

        if (payload.PIC) {
          if (!payload.PIC.Name) throw new Error('PIC Name is required.');
          if (!payload.PIC.ContactNo)
            throw new Error('PIC ContactNo is required.');

          await BusinessContactModel.upsert(
            {
              ContactId: cuid(),
              CustomerId: payload.CustomerId,
              Name: payload.PIC.Name,
              Email: payload.PIC.Email,
              ContactNo: payload.PIC.ContactNo,
              Position: payload.PIC.Position,
              IsMainYN: payload.PIC.IsMainYN,
            },
            { transaction },
          );
        }
      }

      // Addresses
      if (payload.Address?.length) {
        for (const address of payload.Address) {
          await ObjectAddressModel.upsert(
            {
              AddressId: cuid(),
              ObjectId: payload.CustomerId,
              ObjectType: 'Customer',
              AddressLine1: address.AddressLine1,
              AddressLine2: address.AddressLine2,
              City: address.City,
              State: address.State,
              PostalCode: address.PostCode,
              Country: address.Country,
              Latitude: 0,
              Longitude: 0,
              AddressType: (address as any).AddressType ?? 'Billing Address',
              IsDefaultYN: (address as any).IsDefaultYN ?? 'Y',
              Status: 'Active',
              CreatedById: payload.UserId ?? payload.CustomerId,
              CreatedAt: new Date(),
              UpdatedById: payload.UserId ?? payload.CustomerId,
              UpdatedAt: new Date(),
            },
            { transaction },
          );
        }
      }

      // Optional cleanup: (left unimplemented)
      /**
       * If strict sync:
       * Remove any existing business_Contact or object_Address not in the incoming payload (based on CustomerId).
       */

      if (createdTransaction) await transaction.commit();
    } catch (err) {
      if (createdTransaction && transaction) await transaction.rollback();
      console.error('[CustomerBaseWriter.write] failed', {
        ...ctx,
        error: (err as Error)?.message,
      });
      throw err;
    }
  }
}
