import { Op, Transaction } from 'sequelize';
import { CustomerBaseWriter } from '../customer-base-writer/customer-base-writer';
import { SyncLogger } from '../sync-logger/sync-logger';
import { EventTypeEnum } from '../../enum/event-type.enum';
import { TrigerredByEnum } from '../../enum/triggered-by.enum';
import {
  IsSystemActiveEnum,
  IsSystemWantsSyncEnum,
} from '../../enum/registered-system.enum';
import { CustomerBase } from '../../base/customer-base/customer-base.base';
import { ISyncPayload } from '../../types/sync-payload.type';
import { CustomerRegisteredSystemModel } from '../../models';
import { getConnection } from '../../database';

export class CustomerBaseSyncService {
  constructor(
    private writer: CustomerBaseWriter | null,
    private readonly syncLogger: SyncLogger,
  ) {}

  private async getTargetSystems(sourceSystemCode: string): Promise<string[]> {
    try {
      const systems = await CustomerRegisteredSystemModel.findAll({
        where: {
          IsActiveYN: IsSystemActiveEnum.Y,
          WantsSyncYN: IsSystemWantsSyncEnum.Y,
          SystemCode: { [Op.not]: sourceSystemCode },
        },
        attributes: ['SystemCode'],
      });
      return systems.map((s) => s.SystemCode);
    } catch (err) {
      console.error('[CustomerBaseSyncService.getTargetSystems] failed', {
        sourceSystemCode,
        error: (err as Error)?.message,
      });
      throw err;
    }
  }

  private getWriter(): CustomerBaseWriter {
    if (!this.writer) {
      const seq = getConnection();
      if (!seq) {
        throw new Error('CustomerBase DB not initialized on the system yet.');
      }
      this.writer = new CustomerBaseWriter(seq);
    }
    return this.writer;
  }

  public async sync(
    customer: CustomerBase,
    triggeredBy: TrigerredByEnum,
    eventType: EventTypeEnum,
    dbTransaction: Transaction,
  ): Promise<void> {
    const ctx = {
      CustomerId: customer.CustomerId,
      SourceSystemCode: customer.getSourceSystemCode(),
      TriggeredBy: triggeredBy,
      EventType: eventType,
    };

    try {
      const writer = this.getWriter();
      const syncLogger = new SyncLogger();

      const addr = customer.getAddress?.();
      const payload: ISyncPayload = {
        ...customer.getSyncPayload(),
        Address: addr ? [addr] : [],
      };

      await writer.write(payload, dbTransaction);

      const targetSystemCodes = await this.getTargetSystems(
        ctx.SourceSystemCode,
      );

      await syncLogger.logSyncTargets({
        CustomerId: ctx.CustomerId,
        SourceSystemCode: ctx.SourceSystemCode,
        TargetSystemCodes: targetSystemCodes,
        TriggeredBy: triggeredBy,
        EventType: eventType,
        dbTransaction,
      });
    } catch (err) {
      console.error('[CustomerBaseSyncService.sync] failed', {
        ...ctx,
        error: (err as Error)?.message,
      });
      throw err;
    }
  }
}
