import { Response } from "express";
import { AuthenticatedRequest } from "../types/auth";
import { pool } from "../database/db";
import logger from "../utils/logger";

// 레이아웃 목록 조회
export const getLayouts = async (
  req: AuthenticatedRequest,
  res: Response
): Promise<Response> => {
  try {
    const companyCode = req.user?.companyCode;
    const { externalDbConnectionId, warehouseKey } = req.query;

    let query = `
      SELECT 
        l.*,
        u1.user_name as created_by_name,
        u2.user_name as updated_by_name,
        COUNT(o.id) as object_count
      FROM digital_twin_layout l
      LEFT JOIN user_info u1 ON l.created_by = u1.user_id
      LEFT JOIN user_info u2 ON l.updated_by = u2.user_id
      LEFT JOIN digital_twin_objects o ON l.id = o.layout_id
    `;

    const params: any[] = [];
    let paramIndex = 1;

    // 최고 관리자는 모든 레이아웃 조회 가능
    if (companyCode && companyCode !== '*') {
      query += ` WHERE l.company_code = $${paramIndex}`;
      params.push(companyCode);
      paramIndex++;
    } else {
      query += ` WHERE 1=1`;
    }

    if (externalDbConnectionId) {
      query += ` AND l.external_db_connection_id = $${paramIndex}`;
      params.push(externalDbConnectionId);
      paramIndex++;
    }

    if (warehouseKey) {
      query += ` AND l.warehouse_key = $${paramIndex}`;
      params.push(warehouseKey);
      paramIndex++;
    }

    query += `
      GROUP BY l.id, u1.user_name, u2.user_name
      ORDER BY l.updated_at DESC
    `;

    const result = await pool.query(query, params);

    logger.info("레이아웃 목록 조회", {
      companyCode,
      count: result.rowCount,
    });

    return res.json({
      success: true,
      data: result.rows,
    });
  } catch (error: any) {
    logger.error("레이아웃 목록 조회 실패", error);
    return res.status(500).json({
      success: false,
      message: "레이아웃 목록 조회 중 오류가 발생했습니다.",
      error: error.message,
    });
  }
};

// 레이아웃 상세 조회 (객체 포함)
export const getLayoutById = async (
  req: AuthenticatedRequest,
  res: Response
): Promise<Response> => {
  try {
    const companyCode = req.user?.companyCode;
    const { id } = req.params;

    // 레이아웃 기본 정보 - 최고 관리자는 모든 레이아웃 조회 가능
    let layoutQuery: string;
    let layoutParams: any[];

    if (companyCode && companyCode !== '*') {
      layoutQuery = `
        SELECT l.*
        FROM digital_twin_layout l
        WHERE l.id = $1 AND l.company_code = $2
      `;
      layoutParams = [id, companyCode];
    } else {
      layoutQuery = `
        SELECT l.*
        FROM digital_twin_layout l
        WHERE l.id = $1
      `;
      layoutParams = [id];
    }

    const layoutResult = await pool.query(layoutQuery, layoutParams);

    if (layoutResult.rowCount === 0) {
      return res.status(404).json({
        success: false,
        message: "레이아웃을 찾을 수 없습니다.",
      });
    }

    // 배치된 객체들 조회
    const objectsQuery = `
      SELECT *
      FROM digital_twin_objects
      WHERE layout_id = $1
      ORDER BY display_order, created_at
    `;

    const objectsResult = await pool.query(objectsQuery, [id]);

    logger.info("레이아웃 상세 조회", {
      companyCode,
      layoutId: id,
      objectCount: objectsResult.rowCount,
    });

    return res.json({
      success: true,
      data: {
        layout: layoutResult.rows[0],
        objects: objectsResult.rows,
      },
    });
  } catch (error: any) {
    logger.error("레이아웃 상세 조회 실패", error);
    return res.status(500).json({
      success: false,
      message: "레이아웃 조회 중 오류가 발생했습니다.",
      error: error.message,
    });
  }
};

// 레이아웃 생성
export const createLayout = async (
  req: AuthenticatedRequest,
  res: Response
): Promise<Response> => {
  const client = await pool.connect();

  try {
    const companyCode = req.user?.companyCode;
    const userId = req.user?.userId;
    const {
      externalDbConnectionId,
      warehouseKey,
      layoutName,
      description,
      hierarchyConfig,
      objects,
    } = req.body;

    await client.query("BEGIN");

    // 레이아웃 생성
    const layoutQuery = `
      INSERT INTO digital_twin_layout (
        company_code, external_db_connection_id, warehouse_key,
        layout_name, description, hierarchy_config, created_by, updated_by
      )
      VALUES ($1, $2, $3, $4, $5, $6, $7, $7)
      RETURNING *
    `;

    const layoutResult = await client.query(layoutQuery, [
      companyCode,
      externalDbConnectionId,
      warehouseKey,
      layoutName,
      description,
      hierarchyConfig ? JSON.stringify(hierarchyConfig) : null,
      userId,
    ]);

    const layoutId = layoutResult.rows[0].id;

    // 객체들 저장
    if (objects && objects.length > 0) {
      const objectQuery = `
        INSERT INTO digital_twin_objects (
          layout_id, object_type, object_name,
          position_x, position_y, position_z,
          size_x, size_y, size_z,
          rotation, color,
          area_key, loca_key, loc_type,
          material_count, material_preview_height,
          parent_id, display_order, locked,
          hierarchy_level, parent_key, external_key
        )
        VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, $21, $22)
      `;

      for (const obj of objects) {
        await client.query(objectQuery, [
          layoutId,
          obj.type,
          obj.name,
          obj.position.x,
          obj.position.y,
          obj.position.z,
          obj.size.x,
          obj.size.y,
          obj.size.z,
          obj.rotation || 0,
          obj.color,
          obj.areaKey || null,
          obj.locaKey || null,
          obj.locType || null,
          obj.materialCount || 0,
          obj.materialPreview?.height || null,
          obj.parentId || null,
          obj.displayOrder || 0,
          obj.locked || false,
          obj.hierarchyLevel || 1,
          obj.parentKey || null,
          obj.externalKey || null,
        ]);
      }
    }

    await client.query("COMMIT");

    logger.info("레이아웃 생성", {
      companyCode,
      layoutId,
      objectCount: objects?.length || 0,
    });

    return res.status(201).json({
      success: true,
      data: layoutResult.rows[0],
    });
  } catch (error: any) {
    await client.query("ROLLBACK");
    logger.error("레이아웃 생성 실패", error);
    return res.status(500).json({
      success: false,
      message: "레이아웃 생성 중 오류가 발생했습니다.",
      error: error.message,
    });
  } finally {
    client.release();
  }
};

// 레이아웃 수정
export const updateLayout = async (
  req: AuthenticatedRequest,
  res: Response
): Promise<Response> => {
  const client = await pool.connect();

  try {
    const companyCode = req.user?.companyCode;
    const userId = req.user?.userId;
    const { id } = req.params;
    const {
      layoutName,
      description,
      hierarchyConfig,
      externalDbConnectionId,
      warehouseKey,
      objects,
    } = req.body;

    await client.query("BEGIN");

    // 레이아웃 기본 정보 수정
    const updateLayoutQuery = `
      UPDATE digital_twin_layout
      SET layout_name = $1,
          description = $2,
          hierarchy_config = $3,
          external_db_connection_id = $4,
          warehouse_key = $5,
          updated_by = $6,
          updated_at = NOW()
      WHERE id = $7 AND company_code = $8
      RETURNING *
    `;

    const layoutResult = await client.query(updateLayoutQuery, [
      layoutName,
      description,
      hierarchyConfig ? JSON.stringify(hierarchyConfig) : null,
      externalDbConnectionId || null,
      warehouseKey || null,
      userId,
      id,
      companyCode,
    ]);

    if (layoutResult.rowCount === 0) {
      await client.query("ROLLBACK");
      return res.status(404).json({
        success: false,
        message: "레이아웃을 찾을 수 없습니다.",
      });
    }

    // 기존 객체 삭제
    await client.query(
      "DELETE FROM digital_twin_objects WHERE layout_id = $1",
      [id]
    );

    // 새 객체 저장 (부모-자식 관계 처리)
    if (objects && objects.length > 0) {
      const objectQuery = `
        INSERT INTO digital_twin_objects (
          layout_id, object_type, object_name,
          position_x, position_y, position_z,
          size_x, size_y, size_z,
          rotation, color,
          area_key, loca_key, loc_type,
          material_count, material_preview_height,
          parent_id, display_order, locked,
          hierarchy_level, parent_key, external_key
        )
        VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, $21, $22)
        RETURNING id
      `;

      // 임시 ID (음수) → 실제 DB ID 매핑
      const idMapping: { [tempId: number]: number } = {};

      // 1단계: 부모 객체 먼저 저장 (parentId가 없는 것들)
      for (const obj of objects.filter((o) => !o.parentId)) {
        const result = await client.query(objectQuery, [
          id,
          obj.type,
          obj.name,
          obj.position.x,
          obj.position.y,
          obj.position.z,
          obj.size.x,
          obj.size.y,
          obj.size.z,
          obj.rotation || 0,
          obj.color,
          obj.areaKey || null,
          obj.locaKey || null,
          obj.locType || null,
          obj.materialCount || 0,
          obj.materialPreview?.height || null,
          null, // parent_id
          obj.displayOrder || 0,
          obj.locked || false,
          obj.hierarchyLevel || 1,
          obj.parentKey || null,
          obj.externalKey || null,
        ]);

        // 임시 ID와 실제 DB ID 매핑
        if (obj.id) {
          idMapping[obj.id] = result.rows[0].id;
        }
      }

      // 2단계: 자식 객체 저장 (parentId가 있는 것들)
      for (const obj of objects.filter((o) => o.parentId)) {
        const realParentId = idMapping[obj.parentId!] || null;

        await client.query(objectQuery, [
          id,
          obj.type,
          obj.name,
          obj.position.x,
          obj.position.y,
          obj.position.z,
          obj.size.x,
          obj.size.y,
          obj.size.z,
          obj.rotation || 0,
          obj.color,
          obj.areaKey || null,
          obj.locaKey || null,
          obj.locType || null,
          obj.materialCount || 0,
          obj.materialPreview?.height || null,
          realParentId, // 실제 DB ID 사용
          obj.displayOrder || 0,
          obj.locked || false,
          obj.hierarchyLevel || 1,
          obj.parentKey || null,
          obj.externalKey || null,
        ]);
      }
    }

    await client.query("COMMIT");

    logger.info("레이아웃 수정", {
      companyCode,
      layoutId: id,
      objectCount: objects?.length || 0,
    });

    return res.json({
      success: true,
      data: layoutResult.rows[0],
    });
  } catch (error: any) {
    await client.query("ROLLBACK");
    logger.error("레이아웃 수정 실패", error);
    return res.status(500).json({
      success: false,
      message: "레이아웃 수정 중 오류가 발생했습니다.",
      error: error.message,
    });
  } finally {
    client.release();
  }
};

// 레이아웃 삭제
export const deleteLayout = async (
  req: AuthenticatedRequest,
  res: Response
): Promise<Response> => {
  try {
    const companyCode = req.user?.companyCode;
    const { id } = req.params;

    const query = `
      DELETE FROM digital_twin_layout
      WHERE id = $1 AND company_code = $2
      RETURNING id
    `;

    const result = await pool.query(query, [id, companyCode]);

    if (result.rowCount === 0) {
      return res.status(404).json({
        success: false,
        message: "레이아웃을 찾을 수 없습니다.",
      });
    }

    logger.info("레이아웃 삭제", {
      companyCode,
      layoutId: id,
    });

    return res.json({
      success: true,
      message: "레이아웃이 삭제되었습니다.",
    });
  } catch (error: any) {
    logger.error("레이아웃 삭제 실패", error);
    return res.status(500).json({
      success: false,
      message: "레이아웃 삭제 중 오류가 발생했습니다.",
      error: error.message,
    });
  }
};
