/**
 * ZKTeco Integration Service
 * Fetches attendance from ZKTeco devices and syncs to database
 */
import { Injectable, BadRequestException } from '@nestjs/common';
import { PrismaService } from '../prisma/prisma.service';

// eslint-disable-next-line @typescript-eslint/no-require-imports
const ZKAttendanceClient = require('zk-attendance-sdk');

// SDK returns { deviceUserId, recordTime } or { userId, attTime } - support all variants
export interface ZkAttendanceLog {
  userId?: string;
  userid?: string;
  deviceUserId?: string | number;
  attTime?: string;
  timestamp?: string;
  recordTime?: string;
  time?: string;
  uid?: string | number;
}

@Injectable()
export class ZktecoService {
  constructor(private prisma: PrismaService) {}

  async fetchFromDevice(ip: string, port = 4370): Promise<{ count: number; logs: ZkAttendanceLog[] }> {
    const client = new ZKAttendanceClient(ip, port, 5000, 5200);
    try {
      await client.createSocket();
      const result = await client.getAttendances();
      await client.disconnect();

      const logs = result?.data ?? result ?? [];
      const arr = Array.isArray(logs) ? logs : [logs];
      return { count: arr.length, logs: arr };
    } catch (e) {
      throw new BadRequestException(
        `Failed to connect to ZKTeco at ${ip}:${port}: ${e instanceof Error ? e.message : 'Unknown error'}`,
      );
    }
  }

  async syncAttendance(ip: string, port = 4370): Promise<{ imported: number; skipped: number }> {
    const { logs } = await this.fetchFromDevice(ip, port);
    let imported = 0;
    let skipped = 0;

    for (const log of logs) {
      const userId = log.userId ?? log.userid ?? String(log.deviceUserId ?? log.uid ?? '');
      const attTime = log.attTime ?? log.timestamp ?? log.recordTime ?? log.time;
      if (!userId || !attTime) {
        skipped++;
        continue;
      }

      const emp = await this.prisma.employee.findUnique({
        where: { empNo: String(userId).trim() },
      });
      if (!emp) {
        skipped++;
        continue;
      }

      const punchDate = new Date(attTime);
      const existing = await this.prisma.attendance.findFirst({
        where: {
          employeeId: emp.id,
          punchTime: punchDate,
        },
      });
      if (existing) {
        skipped++;
        continue;
      }

      await this.prisma.attendance.create({
        data: {
          employeeId: emp.id,
          punchTime: punchDate,
          punchType: 'in',
          source: 'zkteco',
        },
      });
      imported++;
    }

    return { imported, skipped };
  }

  async getDeviceInfo(ip: string, port = 4370) {
    const client = new ZKAttendanceClient(ip, port, 5000, 5200);
    try {
      await client.createSocket();
      const info = await client.getInfo();
      const users = await client.getUsers();
      const logs = await client.getAttendances();
      await client.disconnect();
      return {
        info: info ?? {},
        userCount: users?.data?.length ?? users?.length ?? 0,
        logCount: logs?.data?.length ?? logs?.length ?? 0,
      };
    } catch (e) {
      throw new BadRequestException(
        `Failed to connect: ${e instanceof Error ? e.message : 'Unknown error'}`,
      );
    }
  }
}
