import type { FastifyInstance } from 'fastify';
import { z } from 'zod';
import { db } from '../db/client.js';

const TEST_APP_TABLE = 'test_app_maintenance_requests';
const TEST_APP_SEED_COUNT = 7000;
const PRIORITY_VALUES = ['긴급', '높음', '보통', '낮음'] as const;
const STATUS_VALUES = ['접수', '배정완료', '조치중', '부품대기', '완료'] as const;
const ISSUE_TYPES = ['센서 오차', '진동 이상', '누유 감지', '온도 상승', '부품 마모', '통신 장애'] as const;
const REQUESTERS = ['김민재', '박서윤', '이도윤', '최하린', '정서준', '한지민'] as const;
const ASSIGNEES = ['윤태호', '장우진', '서가은', '임현수', '강다온', '문시우'] as const;
const LINE_EQUIPMENT_MAP = {
  PKG: ['실링기 1호', '실링기 2호', '포장로봇 1호', '라벨러 2호'],
  MFG: ['혼합기 A', '혼합기 B', '압출기 3호', '컨베이어 7호'],
  UTL: ['냉각펌프 1호', '공조기 2호', '콤프레서 1호', '보일러 1호'],
  QC: ['비전검사기 1호', '중량선별기 2호', '샘플러 1호', '검사컨베이어 1호'],
} as const;
const LINE_CODES = Object.keys(LINE_EQUIPMENT_MAP) as Array<keyof typeof LINE_EQUIPMENT_MAP>;

const listQuerySchema = z.object({
  page: z.coerce.number().int().min(1).default(1),
  pageSize: z.coerce.number().int().min(1).max(200).default(100),
  keyword: z.string().trim().default(''),
  lineCode: z.string().trim().optional(),
  priority: z.string().trim().optional(),
  status: z.string().trim().optional(),
  requestedFrom: z.string().trim().optional(),
  requestedTo: z.string().trim().optional(),
});

const updateItemSchema = z.object({
  id: z.coerce.number().int().positive(),
  priority: z.enum(PRIORITY_VALUES),
  status: z.enum(STATUS_VALUES),
  assigneeName: z.string().trim().max(80).optional(),
});

const saveBodySchema = z.object({
  items: z.array(updateItemSchema).min(1).max(200),
});

const deleteParamsSchema = z.object({
  id: z.coerce.number().int().positive(),
});

type TestAppRow = {
  id: number;
  request_no: string;
  line_code: string;
  equipment_name: string;
  issue_type: (typeof ISSUE_TYPES)[number];
  priority: (typeof PRIORITY_VALUES)[number];
  requester_name: string;
  assignee_name: string | null;
  status: (typeof STATUS_VALUES)[number];
  requested_at: Date | string;
  last_action_at: Date | string;
};

function pad2(value: number) {
  return String(value).padStart(2, '0');
}

function formatDateTime(value: Date | string) {
  const date = value instanceof Date ? value : new Date(value);

  if (Number.isNaN(date.getTime())) {
    return '';
  }

  return `${date.getFullYear()}-${pad2(date.getMonth() + 1)}-${pad2(date.getDate())} ${pad2(date.getHours())}:${pad2(date.getMinutes())}:${pad2(date.getSeconds())}`;
}

function toResponseRow(row: TestAppRow) {
  return {
    id: row.id,
    requestNo: row.request_no,
    lineCode: row.line_code,
    equipmentName: row.equipment_name,
    issueType: row.issue_type,
    priority: row.priority,
    requesterName: row.requester_name,
    assigneeName: row.assignee_name ?? '',
    status: row.status,
    requestedAt: formatDateTime(row.requested_at),
    lastActionAt: formatDateTime(row.last_action_at),
  };
}

function pickPriority(index: number) {
  const ratio = index % 100;

  if (ratio < 6) {
    return '긴급' as const;
  }

  if (ratio < 24) {
    return '높음' as const;
  }

  if (ratio < 72) {
    return '보통' as const;
  }

  return '낮음' as const;
}

function pickStatus(index: number, priority: (typeof PRIORITY_VALUES)[number]) {
  const ratio = index % 100;

  if (priority === '긴급') {
    if (ratio < 28) {
      return '접수' as const;
    }

    if (ratio < 54) {
      return '배정완료' as const;
    }

    if (ratio < 86) {
      return '조치중' as const;
    }

    if (ratio < 93) {
      return '부품대기' as const;
    }

    return '완료' as const;
  }

  if (ratio < 22) {
    return '접수' as const;
  }

  if (ratio < 45) {
    return '배정완료' as const;
  }

  if (ratio < 70) {
    return '조치중' as const;
  }

  if (ratio < 79) {
    return '부품대기' as const;
  }

  return '완료' as const;
}

function buildSeedRow(index: number) {
  const lineCode = LINE_CODES[index % LINE_CODES.length];
  const equipmentList = LINE_EQUIPMENT_MAP[lineCode];
  const equipmentName = equipmentList[Math.floor(index / LINE_CODES.length) % equipmentList.length];
  const priority = pickPriority(index);
  const status = pickStatus(index, priority);
  const issueType = ISSUE_TYPES[(index * 3 + Math.floor(index / 7)) % ISSUE_TYPES.length];
  const requesterName = REQUESTERS[(index * 5 + 1) % REQUESTERS.length];
  const assigneeName = status === '접수' ? null : ASSIGNEES[(index * 7 + 2) % ASSIGNEES.length];
  const requestedAt = new Date(Date.now() - ((index % (45 * 48)) * 30 + (index % 3) * 10) * 60_000);
  const lastActionAt =
    status === '접수'
      ? requestedAt
      : new Date(requestedAt.getTime() + (((index % 9) + 1) * 45 + (priority === '긴급' ? 20 : 0)) * 60_000);
  const requestNo = `JR-${requestedAt.getFullYear()}${pad2(requestedAt.getMonth() + 1)}${pad2(requestedAt.getDate())}-${String(1000 + index).padStart(4, '0')}`;

  return {
    request_no: requestNo,
    line_code: lineCode,
    equipment_name: equipmentName,
    issue_type: issueType,
    priority,
    requester_name: requesterName,
    assignee_name: assigneeName,
    status,
    requested_at: requestedAt,
    last_action_at: lastActionAt,
  };
}

async function ensureTestAppTable() {
  const tableExists = await db.schema.hasTable(TEST_APP_TABLE);

  if (!tableExists) {
    await db.schema.createTable(TEST_APP_TABLE, (table) => {
      table.increments('id').primary();
      table.string('request_no', 40).notNullable().unique();
      table.string('line_code', 20).notNullable();
      table.string('equipment_name', 120).notNullable();
      table.string('issue_type', 80).notNullable();
      table.string('priority', 20).notNullable();
      table.string('requester_name', 80).notNullable();
      table.string('assignee_name', 80).nullable();
      table.string('status', 40).notNullable();
      table.timestamp('requested_at').notNullable().defaultTo(db.fn.now());
      table.timestamp('last_action_at').notNullable().defaultTo(db.fn.now());
      table.index(['requested_at', 'id'], 'test_app_requests_requested_at_idx');
      table.index(['line_code', 'priority', 'status'], 'test_app_requests_filter_idx');
    });
  }

  const countResult = await db(TEST_APP_TABLE).count<{ count: string }[]>({ count: '*' }).first();
  const currentCount = Number(countResult?.count ?? 0);

  if (currentCount >= TEST_APP_SEED_COUNT) {
    return;
  }

  const chunkSize = 500;
  const rowsToInsert = Array.from({ length: TEST_APP_SEED_COUNT - currentCount }, (_, offset) =>
    buildSeedRow(currentCount + offset),
  );

  for (let index = 0; index < rowsToInsert.length; index += chunkSize) {
    await db(TEST_APP_TABLE).insert(rowsToInsert.slice(index, index + chunkSize));
  }
}

function applyListFilters(baseQuery: ReturnType<typeof db>, query: z.infer<typeof listQuerySchema>) {
  if (query.keyword) {
    baseQuery.where((builder) => {
      builder
        .whereILike('request_no', `%${query.keyword}%`)
        .orWhereILike('equipment_name', `%${query.keyword}%`)
        .orWhereILike('requester_name', `%${query.keyword}%`);
    });
  }

  if (query.lineCode && query.lineCode !== '전체') {
    baseQuery.where('line_code', query.lineCode);
  }

  if (query.priority && query.priority !== '전체') {
    baseQuery.where('priority', query.priority);
  }

  if (query.status && query.status !== '전체') {
    baseQuery.where('status', query.status);
  }

  if (query.requestedFrom) {
    baseQuery.where('requested_at', '>=', new Date(`${query.requestedFrom}T00:00:00+09:00`));
  }

  if (query.requestedTo) {
    baseQuery.where('requested_at', '<=', new Date(`${query.requestedTo}T23:59:59+09:00`));
  }
}

async function handleListRequests(request: { query?: unknown }) {
  await ensureTestAppTable();

  const query = listQuerySchema.parse(request.query ?? {});
  const offset = (query.page - 1) * query.pageSize;
  const baseQuery = db(TEST_APP_TABLE);

  applyListFilters(baseQuery, query);

  const [countResult, rows] = await Promise.all([
    baseQuery.clone().count<{ count: string }[]>({ count: '*' }).first(),
    baseQuery
      .clone()
      .select<TestAppRow[]>('*')
      .orderBy('requested_at', 'desc')
      .orderBy('id', 'desc')
      .limit(query.pageSize)
      .offset(offset),
  ]);

  const total = Number(countResult?.count ?? 0);

  return {
    ok: true,
    items: rows.map(toResponseRow),
    page: query.page,
    pageSize: query.pageSize,
    total,
    hasNext: offset + rows.length < total,
    filters: {
      keyword: query.keyword,
      lineCode: query.lineCode ?? '전체',
      priority: query.priority ?? '전체',
      status: query.status ?? '전체',
      requestedFrom: query.requestedFrom ?? null,
      requestedTo: query.requestedTo ?? null,
    },
  };
}

async function handleSaveRequests(request: { body?: unknown }) {
  await ensureTestAppTable();

  const payload = saveBodySchema.parse(request.body ?? {});
  const ids = payload.items.map((item) => item.id);
  const existingRows = await db(TEST_APP_TABLE).select<TestAppRow[]>('*').whereIn('id', ids);
  const existingIdSet = new Set(existingRows.map((row) => row.id));
  const missingIds = ids.filter((id) => !existingIdSet.has(id));

  if (missingIds.length) {
    const error = new Error(`저장할 요청을 찾을 수 없습니다: ${missingIds.join(', ')}`) as Error & { statusCode?: number };
    error.statusCode = 404;
    throw error;
  }

  const updatedRows: TestAppRow[] = [];

  for (const item of payload.items) {
    const [updatedRow] = await db(TEST_APP_TABLE)
      .where({ id: item.id })
      .update(
        {
          priority: item.priority,
          status: item.status,
          assignee_name: item.assigneeName?.trim() || null,
          last_action_at: db.fn.now(),
        },
        '*',
      );

    if (updatedRow) {
      updatedRows.push(updatedRow as TestAppRow);
    }
  }

  return {
    ok: true,
    count: updatedRows.length,
    items: updatedRows.map(toResponseRow),
  };
}

async function handleDeleteRequest(request: { params?: unknown }) {
  await ensureTestAppTable();

  const { id } = deleteParamsSchema.parse(request.params ?? {});
  const [deletedRow] = await db(TEST_APP_TABLE).where({ id }).delete('*');

  if (!deletedRow) {
    const error = new Error(`삭제할 요청을 찾을 수 없습니다: ${id}`) as Error & { statusCode?: number };
    error.statusCode = 404;
    throw error;
  }

  return {
    ok: true,
    deletedId: id,
    item: toResponseRow(deletedRow as TestAppRow),
  };
}

export async function registerTestAppRoutes(app: FastifyInstance) {
  app.get('/api/test-app/maintenance-requests', handleListRequests);
  app.get('/api/test-app/measurements', handleListRequests);

  app.put('/api/test-app/maintenance-requests', handleSaveRequests);
  app.put('/api/test-app/measurements', handleSaveRequests);
  app.delete('/api/test-app/maintenance-requests/:id', handleDeleteRequest);
}
