import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { WorkflowVariableMaster } from 'src/common/entities';
import { CommonDataService } from 'src/common/services/commonData.service';
import { AppLoggerService } from 'src/common/services/logger.service';
import { handleKnownErrors } from 'src/common/utils/handle-error.util';
import { ERROR_CODES } from 'src/common/constants/error-string-constants';

@Injectable()
export class WorkflowVariableMasterRepository {
  constructor(
    @InjectRepository(WorkflowVariableMaster)
    private readonly repo: Repository<WorkflowVariableMaster>,
    private readonly commonDataService: CommonDataService,
    private readonly logger: AppLoggerService,
  ) {}

  async findByKey(variableKey: string): Promise<WorkflowVariableMaster | null> {
    try {
      const data = await this.commonDataService.get(
        this.repo,
        undefined,
        { variableKey, isActive: true },
        1,
        0,
        { id: 'DESC' },
      );
      return data[0] ?? null;
    } catch (error) {
      this.logger.error('Failed to load workflow variable by key', error?.stack, { variableKey });
      handleKnownErrors(ERROR_CODES.WORKFLOW_VARIABLE_GET_FAILED, error);
    }
  }

  async findByKeys(variableKeys: string[]): Promise<WorkflowVariableMaster[]> {
    try {
      return await this.repo
        .createQueryBuilder('wvm')
        .where('wvm.variableKey IN (:...keys)', { keys: variableKeys })
        .andWhere('wvm.isActive = :isActive', { isActive: true })
        .andWhere('wvm.deletedAt IS NULL')
        .orderBy('wvm.executionOrder', 'ASC')
        .getMany();
    } catch (error) {
      this.logger.error('Failed to load workflow variables by keys', error?.stack, { variableKeys });
      handleKnownErrors(ERROR_CODES.WORKFLOW_VARIABLE_GET_FAILED, error);
    }
  }

  async findActiveBySourceTable(sourceTable: string): Promise<WorkflowVariableMaster[]> {
    try {
      return await this.commonDataService.get(
        this.repo,
        undefined,
        { sourceTable, isActive: true },
        undefined,
        undefined,
        { executionOrder: 'ASC' },
      );
    } catch (error) {
      this.logger.error('Failed to load workflow variables by source table', error?.stack, { sourceTable });
      handleKnownErrors(ERROR_CODES.WORKFLOW_VARIABLE_GET_FAILED, error);
    }
  }

  async findAllActive(): Promise<WorkflowVariableMaster[]> {
    try {
      return await this.commonDataService.get(
        this.repo,
        undefined,
        { isActive: true },
        undefined,
        undefined,
        { executionOrder: 'ASC' },
      );
    } catch (error) {
      this.logger.error('Failed to load all active workflow variables', error?.stack);
      handleKnownErrors(ERROR_CODES.WORKFLOW_VARIABLE_GET_FAILED, error);
    }
  }

  /**
   * Find all DEFAULT variables (variables marked as default in variable_scope)
   * These are used as fallback when a workflow has no specific variables configured
   */
  async findDefaultVariables(): Promise<WorkflowVariableMaster[]> {
    try {
      return await this.repo
        .createQueryBuilder('wvm')
        .where('wvm.isActive = :isActive', { isActive: true })
        .andWhere('wvm.variableScope = :variableScope', { variableScope: 'DEFAULT' })
        .andWhere('wvm.deletedAt IS NULL')
        .orderBy('wvm.executionOrder', 'ASC')
        .addOrderBy('wvm.variableKey', 'ASC')
        .getMany();
    } catch (error) {
      this.logger.error('Failed to load default workflow variables', error?.stack);
      handleKnownErrors(ERROR_CODES.WORKFLOW_VARIABLE_GET_FAILED, error);
    }
  }

  async create(data: Partial<WorkflowVariableMaster>): Promise<WorkflowVariableMaster> {
    try {
      const variable = this.repo.create(data);
      return await this.repo.save(variable);
    } catch (error) {
      this.logger.error('Failed to create workflow variable', error?.stack, { data });
      handleKnownErrors(ERROR_CODES.WORKFLOW_VARIABLE_CREATE_FAILED, error);
    }
  }

  async update(id: number, data: Partial<WorkflowVariableMaster>): Promise<WorkflowVariableMaster> {
    try {
      await this.repo.update(id, data as any);
      const updated = await this.repo.findOne({ where: { id } });
      if (!updated) {
        throw new Error('Variable not found after update');
      }
      return updated;
    } catch (error) {
      this.logger.error('Failed to update workflow variable', error?.stack, { id, data });
      handleKnownErrors(ERROR_CODES.WORKFLOW_VARIABLE_UPDATE_FAILED, error);
    }
  }
}
