import {
    Table,
    Column,
    CreatedAt,
    UpdatedAt,
    DeletedAt,
    Model,
    ForeignKey,
    PrimaryKey,
    AutoIncrement,
    DataType,
    BelongsTo,
    AfterUpdate,
    AfterBulkCreate,
    AfterDestroy,
    AfterCreate,
    AfterUpsert,
} from 'sequelize-typescript';
import { Container } from 'typedi';
import AuditLogService from '../../../audit/business/services/auditLogService';
import { InstanceDAO } from '../../../instances/infrastructure/dataAccessObjects/instanceDAO';
import { SiteDAO } from '../../../regions/infrastructure/dataAccessObjects/siteDAO';
import { AutoCloseFallback } from '../../business/enum/timeTracking/autoCloseFallback';
import { TimeTrackingEntryType } from '../../business/types/entry';
import { TimeTrackingEntrySource } from '../../business/types/source';
import { TimeTrackingDaySummaryDAO } from './daySummary';
import { TimeTrackingKioskDAO } from './kiosk';

export interface TimeTrackingEntryCreationAttributes {
    time: Date;
    userId: number;
    type: TimeTrackingEntryType;
    latitude?: number;
    longitude?: number;
    siteId?: number;
    source: TimeTrackingEntrySource;
    createdBy?: number;
    updatedBy?: number;
    summaryId?: number;
    comment?: string;
    facialRecognitionSkipped?: boolean;
    kioskId?: number;
    instanceId?: number;
    eventTime?: Date | null;
    desertEagleEntryId?: string;
    isRemoteWork?: boolean;
    autoCloseFallback?: AutoCloseFallback | null;
}
const tableName = 'TimeTrackingEntries';

@Table({
    tableName,
})
export class TimeTrackingEntryDAO extends Model<TimeTrackingEntryDAO, TimeTrackingEntryCreationAttributes> {
    @PrimaryKey
    @AutoIncrement
    @Column
    id!: number;

    @CreatedAt
    @Column(DataType.DATE)
    createdAt!: Date;

    @UpdatedAt
    @Column(DataType.DATE)
    updatedAt!: Date;

    @DeletedAt
    @Column(DataType.DATE)
    deletedAt!: Date | null;

    @Column
    @ForeignKey(() => SiteDAO)
    siteId?: number;

    @Column(DataType.DATE)
    time!: Date;

    @Column(DataType.ENUM(...Object.values(TimeTrackingEntryType)))
    type!: TimeTrackingEntryType;

    @Column
    userId!: number;

    @Column
    createdBy?: number;

    @Column
    deletedBy?: number;

    @Column
    updatedBy?: number;

    @Column
    latitude?: number;

    @Column
    longitude?: number;

    @Column(DataType.ENUM(...Object.values(TimeTrackingEntrySource)))
    source!: TimeTrackingEntrySource;

    @Column
    @ForeignKey(() => TimeTrackingDaySummaryDAO)
    summaryId?: number;

    @BelongsTo(() => TimeTrackingDaySummaryDAO)
    summary?: TimeTrackingDaySummaryDAO;

    @Column
    comment?: string;

    @Column({ defaultValue: false })
    facialRecognitionSkipped?: boolean;

    @Column
    @ForeignKey(() => TimeTrackingKioskDAO)
    kioskId?: number;

    @BelongsTo(() => TimeTrackingKioskDAO)
    kiosk?: TimeTrackingKioskDAO;

    @Column
    @ForeignKey(() => InstanceDAO)
    instanceId?: number;

    @BelongsTo(() => InstanceDAO)
    instance?: InstanceDAO;

    @Column(DataType.DATE)
    eventTime?: Date | null;

    @Column({ allowNull: true, type: DataType.STRING })
    desertEagleEntryId?: string | null;

    @Column({ defaultValue: false, type: DataType.BOOLEAN })
    isRemoteWork?: boolean;

    @Column({ allowNull: true, type: DataType.STRING })
    autoCloseFallback!: AutoCloseFallback | null;

    @AfterCreate
    static async afterCreateHook(entry: TimeTrackingEntryDAO) {
        const auditLogService = Container.get(AuditLogService);
        const record = entry.get({ plain: true }) as unknown as Record<string, unknown>;
        await auditLogService.processCreate(tableName, record);
    }

    @AfterUpdate
    static async afterUpdateHook(entry: TimeTrackingEntryDAO) {
        const auditLogService = Container.get(AuditLogService);
        const record = entry.get({ plain: true }) as unknown as Record<string, unknown>;
        await auditLogService.processUpdate(tableName, record);
    }

    @AfterUpsert
    static async afterUpsertHook(result: [TimeTrackingEntryDAO | null, boolean | undefined]) {
        const [entry] = result;
        if (!entry) return;
        const created = entry.createdAt === entry.updatedAt;
        const record = entry.get({ plain: true }) as unknown as Record<string, unknown>;
        const auditLogService = Container.get(AuditLogService);
        if (created) {
            await auditLogService.processCreate(tableName, record);
        } else {
            await auditLogService.processUpdate(tableName, record);
        }
    }

    @AfterDestroy
    static async afterDestroyHook(entry: TimeTrackingEntryDAO) {
        const auditLogService = Container.get(AuditLogService);
        const record = entry.get({ plain: true }) as unknown as Record<string, unknown>;
        await auditLogService.processDelete(tableName, record);
    }

    @AfterBulkCreate
    static async afterBulkCreateHook(entries: TimeTrackingEntryDAO[]) {
        const auditLogService = Container.get(AuditLogService);
        const records = entries.filter((entry) => entry.id).map((entry) => entry.get({ plain: true }) as unknown as Record<string, unknown>);
        await auditLogService.processBulkCreate(tableName, records);
    }
}
