// SPDX-FileCopyrightText: 2025 Contributors to the CitrineOS Project
//
// SPDX-License-Identifier: Apache-2.0

import type {
  ComponentDto,
  MessageInfoDto,
  TenantDto,
  VariableAttributeDto,
  VariableDto,
  VariableMonitoringDto,
} from '@citrineos/base';
import { DEFAULT_TENANT_ID, OCPP2_0_1, OCPP2_Namespace } from '@citrineos/base';
import {
  BeforeCreate,
  BeforeUpdate,
  BelongsTo,
  BelongsToMany,
  Column,
  DataType,
  ForeignKey,
  HasMany,
  Model,
  Table,
} from 'sequelize-typescript';

import { MessageInfo } from '../MessageInfo/MessageInfo.js';
import { Tenant } from '../Tenant.js';
import { VariableMonitoring } from '../VariableMonitoring/VariableMonitoring.js';
import { ComponentVariable } from './ComponentVariable.js';
import { EvseType } from './EvseType.js';
import { Variable } from './Variable.js';
import { VariableAttribute } from './VariableAttribute.js';

@Table({
  indexes: [
    {
      unique: true,
      name: 'components_tenantId_name',
      fields: ['tenantId', 'name'],
      where: {
        instance: null,
      },
    },
  ],
})
export class Component extends Model implements OCPP2_0_1.ComponentType, ComponentDto {
  static readonly MODEL_NAME: string = OCPP2_Namespace.ComponentType;

  /**
   * Fields
   */

  @Column({
    type: DataType.STRING,
    unique: 'tenantId_name_instance',
  })
  declare name: string;

  @Column({
    type: DataType.STRING,
    unique: 'tenantId_name_instance',
  })
  declare instance?: string | null;

  /**
   * Relations
   */

  @BelongsTo(() => EvseType, 'evseDatabaseId')
  declare evse?: EvseType;

  @ForeignKey(() => EvseType)
  @Column(DataType.INTEGER)
  declare evseDatabaseId?: number | null;

  @BelongsToMany(() => Variable, { through: () => ComponentVariable, foreignKey: 'componentId' })
  declare variables?: VariableDto[];

  declare customData?: OCPP2_0_1.CustomDataType | null;

  // Declare the association methods, to be automatically generated by Sequelize at runtime
  public addVariable!: (variable: VariableDto) => Promise<void>;
  public getVariables!: () => Promise<VariableDto[]>;

  @HasMany(() => VariableAttribute, 'componentId')
  declare variableAttributes?: VariableAttributeDto[];

  @HasMany(() => VariableMonitoring, 'componentId')
  declare variableMonitorings?: VariableMonitoringDto[];

  @HasMany(() => MessageInfo, 'displayComponentId')
  declare messageInfos?: MessageInfoDto[];

  @ForeignKey(() => Tenant)
  @Column({
    type: DataType.INTEGER,
    allowNull: false,
    unique: 'tenantId_name_instance',
    onUpdate: 'CASCADE',
    onDelete: 'RESTRICT',
  })
  declare tenantId: number;

  @BelongsTo(() => Tenant, 'tenantId')
  declare tenant?: TenantDto;

  @BeforeUpdate
  @BeforeCreate
  static setDefaultTenant(instance: Component) {
    if (instance.tenantId == null) {
      instance.tenantId = DEFAULT_TENANT_ID;
    }
  }

  constructor(...args: any[]) {
    super(...args);
    if (this.tenantId == null) {
      this.tenantId = DEFAULT_TENANT_ID;
    }
  }
}
