import { query, queryOne } from "../database/db";
import {
  CreateLayoutRequest,
  UpdateLayoutRequest,
  LayoutStandard,
  LayoutType,
  LayoutCategory,
} from "../types/layout";

// JSON 데이터를 안전하게 파싱하는 헬퍼 함수
function safeJSONParse(data: any): any {
  if (data === null || data === undefined) {
    return null;
  }

  // 이미 객체인 경우 그대로 반환
  if (typeof data === "object") {
    return data;
  }

  // 문자열인 경우 파싱 시도
  if (typeof data === "string") {
    try {
      return JSON.parse(data);
    } catch (error) {
      console.error("JSON 파싱 오류:", error, "Data:", data);
      return null;
    }
  }

  return data;
}

// JSON 데이터를 안전하게 문자열화하는 헬퍼 함수
function safeJSONStringify(data: any): string | null {
  if (data === null || data === undefined) {
    return null;
  }

  // 이미 문자열인 경우 그대로 반환
  if (typeof data === "string") {
    return data;
  }

  // 객체인 경우 문자열로 변환
  try {
    return JSON.stringify(data);
  } catch (error) {
    console.error("JSON 문자열화 오류:", error, "Data:", data);
    return null;
  }
}

export class LayoutService {
  /**
   * 레이아웃 목록 조회
   */
  async getLayouts(params: {
    page?: number;
    size?: number;
    category?: string;
    layoutType?: string;
    searchTerm?: string;
    companyCode: string;
    includePublic?: boolean;
  }): Promise<{ data: LayoutStandard[]; total: number }> {
    const {
      page = 1,
      size = 20,
      category,
      layoutType,
      searchTerm,
      companyCode,
      includePublic = true,
    } = params;

    const skip = (page - 1) * size;

    // 동적 WHERE 조건 구성
    const whereConditions: string[] = ["is_active = $1"];
    const values: any[] = ["Y"];
    let paramIndex = 2;

    // company_code OR is_public 조건
    if (includePublic) {
      whereConditions.push(
        `(company_code = $${paramIndex} OR is_public = $${paramIndex + 1})`
      );
      values.push(companyCode, "Y");
      paramIndex += 2;
    } else {
      whereConditions.push(`company_code = $${paramIndex++}`);
      values.push(companyCode);
    }

    if (category) {
      whereConditions.push(`category = $${paramIndex++}`);
      values.push(category);
    }

    if (layoutType) {
      whereConditions.push(`layout_type = $${paramIndex++}`);
      values.push(layoutType);
    }

    if (searchTerm) {
      whereConditions.push(
        `(layout_name ILIKE $${paramIndex} OR layout_name_eng ILIKE $${paramIndex} OR description ILIKE $${paramIndex})`
      );
      values.push(`%${searchTerm}%`);
      paramIndex++;
    }

    const whereClause = `WHERE ${whereConditions.join(" AND ")}`;

    const [data, countResult] = await Promise.all([
      query<any>(
        `SELECT * FROM layout_standards 
         ${whereClause}
         ORDER BY sort_order ASC, created_date DESC
         LIMIT $${paramIndex++} OFFSET $${paramIndex++}`,
        [...values, size, skip]
      ),
      queryOne<{ count: string }>(
        `SELECT COUNT(*) as count FROM layout_standards ${whereClause}`,
        values
      ),
    ]);

    const total = parseInt(countResult?.count || "0");

    return {
      data: data.map(
        (layout) =>
          ({
            layoutCode: layout.layout_code,
            layoutName: layout.layout_name,
            layoutNameEng: layout.layout_name_eng,
            description: layout.description,
            layoutType: layout.layout_type as LayoutType,
            category: layout.category as LayoutCategory,
            iconName: layout.icon_name,
            defaultSize: safeJSONParse(layout.default_size),
            layoutConfig: safeJSONParse(layout.layout_config),
            zonesConfig: safeJSONParse(layout.zones_config),
            previewImage: layout.preview_image,
            sortOrder: layout.sort_order,
            isActive: layout.is_active,
            isPublic: layout.is_public,
            companyCode: layout.company_code,
            createdDate: layout.created_date,
            createdBy: layout.created_by,
            updatedDate: layout.updated_date,
            updatedBy: layout.updated_by,
          }) as LayoutStandard
      ),
      total,
    };
  }

  /**
   * 레이아웃 상세 조회
   */
  async getLayoutById(
    layoutCode: string,
    companyCode: string
  ): Promise<LayoutStandard | null> {
    const layout = await queryOne<any>(
      `SELECT * FROM layout_standards 
       WHERE layout_code = $1 AND is_active = $2 
       AND (company_code = $3 OR is_public = $4)
       LIMIT 1`,
      [layoutCode, "Y", companyCode, "Y"]
    );

    if (!layout) return null;

    return {
      layoutCode: layout.layout_code,
      layoutName: layout.layout_name,
      layoutNameEng: layout.layout_name_eng,
      description: layout.description,
      layoutType: layout.layout_type as LayoutType,
      category: layout.category as LayoutCategory,
      iconName: layout.icon_name,
      defaultSize: safeJSONParse(layout.default_size),
      layoutConfig: safeJSONParse(layout.layout_config),
      zonesConfig: safeJSONParse(layout.zones_config),
      previewImage: layout.preview_image,
      sortOrder: layout.sort_order,
      isActive: layout.is_active,
      isPublic: layout.is_public,
      companyCode: layout.company_code,
      createdDate: layout.created_date,
      createdBy: layout.created_by,
      updatedDate: layout.updated_date,
      updatedBy: layout.updated_by,
    } as LayoutStandard;
  }

  /**
   * 레이아웃 생성
   */
  async createLayout(
    request: CreateLayoutRequest,
    companyCode: string,
    userId: string
  ): Promise<LayoutStandard> {
    // 레이아웃 코드 생성 (자동)
    const layoutCode = await this.generateLayoutCode(
      request.layoutType,
      companyCode
    );

    const layout = await queryOne<any>(
      `INSERT INTO layout_standards 
       (layout_code, layout_name, layout_name_eng, description, layout_type, category,
        icon_name, default_size, layout_config, zones_config, is_public, is_active,
        company_code, created_by, updated_by, created_date, updated_date, sort_order)
       VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, NOW(), NOW(), 0)
       RETURNING *`,
      [
        layoutCode,
        request.layoutName,
        request.layoutNameEng,
        request.description,
        request.layoutType,
        request.category,
        request.iconName,
        safeJSONStringify(request.defaultSize),
        safeJSONStringify(request.layoutConfig),
        safeJSONStringify(request.zonesConfig),
        request.isPublic ? "Y" : "N",
        "Y",
        companyCode,
        userId,
        userId,
      ]
    );

    return this.mapToLayoutStandard(layout);
  }

  /**
   * 레이아웃 수정
   */
  async updateLayout(
    request: UpdateLayoutRequest,
    companyCode: string,
    userId: string
  ): Promise<LayoutStandard | null> {
    // 수정 권한 확인
    const existing = await queryOne<any>(
      `SELECT * FROM layout_standards 
       WHERE layout_code = $1 AND company_code = $2 AND is_active = $3`,
      [request.layoutCode, companyCode, "Y"]
    );

    if (!existing) {
      throw new Error("레이아웃을 찾을 수 없거나 수정 권한이 없습니다.");
    }

    // 동적 UPDATE 쿼리 생성
    const updateFields: string[] = ["updated_by = $1", "updated_date = NOW()"];
    const values: any[] = [userId];
    let paramIndex = 2;

    if (request.layoutName !== undefined) {
      updateFields.push(`layout_name = $${paramIndex++}`);
      values.push(request.layoutName);
    }
    if (request.layoutNameEng !== undefined) {
      updateFields.push(`layout_name_eng = $${paramIndex++}`);
      values.push(request.layoutNameEng);
    }
    if (request.description !== undefined) {
      updateFields.push(`description = $${paramIndex++}`);
      values.push(request.description);
    }
    if (request.layoutType !== undefined) {
      updateFields.push(`layout_type = $${paramIndex++}`);
      values.push(request.layoutType);
    }
    if (request.category !== undefined) {
      updateFields.push(`category = $${paramIndex++}`);
      values.push(request.category);
    }
    if (request.iconName !== undefined) {
      updateFields.push(`icon_name = $${paramIndex++}`);
      values.push(request.iconName);
    }
    if (request.defaultSize !== undefined) {
      updateFields.push(`default_size = $${paramIndex++}`);
      values.push(safeJSONStringify(request.defaultSize));
    }
    if (request.layoutConfig !== undefined) {
      updateFields.push(`layout_config = $${paramIndex++}`);
      values.push(safeJSONStringify(request.layoutConfig));
    }
    if (request.zonesConfig !== undefined) {
      updateFields.push(`zones_config = $${paramIndex++}`);
      values.push(safeJSONStringify(request.zonesConfig));
    }
    if (request.isPublic !== undefined) {
      updateFields.push(`is_public = $${paramIndex++}`);
      values.push(request.isPublic ? "Y" : "N");
    }

    const updated = await queryOne<any>(
      `UPDATE layout_standards 
       SET ${updateFields.join(", ")}
       WHERE layout_code = $${paramIndex}
       RETURNING *`,
      [...values, request.layoutCode]
    );

    return this.mapToLayoutStandard(updated);
  }

  /**
   * 레이아웃 삭제 (소프트 삭제)
   */
  async deleteLayout(
    layoutCode: string,
    companyCode: string,
    userId: string
  ): Promise<boolean> {
    const existing = await queryOne<any>(
      `SELECT * FROM layout_standards 
       WHERE layout_code = $1 AND company_code = $2 AND is_active = $3`,
      [layoutCode, companyCode, "Y"]
    );

    if (!existing) {
      throw new Error("레이아웃을 찾을 수 없거나 삭제 권한이 없습니다.");
    }

    await query(
      `UPDATE layout_standards 
       SET is_active = $1, updated_by = $2, updated_date = NOW()
       WHERE layout_code = $3`,
      ["N", userId, layoutCode]
    );

    return true;
  }

  /**
   * 레이아웃 복제
   */
  async duplicateLayout(
    layoutCode: string,
    newName: string,
    companyCode: string,
    userId: string
  ): Promise<LayoutStandard> {
    const original = await this.getLayoutById(layoutCode, companyCode);
    if (!original) {
      throw new Error("복제할 레이아웃을 찾을 수 없습니다.");
    }

    const duplicateRequest: CreateLayoutRequest = {
      layoutName: newName,
      layoutNameEng: original.layoutNameEng
        ? `${original.layoutNameEng} Copy`
        : undefined,
      description: original.description,
      layoutType: original.layoutType,
      category: original.category,
      iconName: original.iconName,
      defaultSize: original.defaultSize,
      layoutConfig: original.layoutConfig,
      zonesConfig: original.zonesConfig,
      isPublic: false, // 복제본은 비공개로 시작
    };

    return this.createLayout(duplicateRequest, companyCode, userId);
  }

  /**
   * 카테고리별 레이아웃 개수 조회
   */
  async getLayoutCountsByCategory(
    companyCode: string
  ): Promise<Record<string, number>> {
    const counts = await query<{ category: string; count: string }>(
      `SELECT category, COUNT(*) as count
       FROM layout_standards
       WHERE is_active = $1 AND (company_code = $2 OR is_public = $3)
       GROUP BY category`,
      ["Y", companyCode, "Y"]
    );

    return counts.reduce(
      (acc: Record<string, number>, item: any) => {
        acc[item.category] = parseInt(item.count);
        return acc;
      },
      {} as Record<string, number>
    );
  }

  /**
   * 레이아웃 코드 자동 생성
   */
  private async generateLayoutCode(
    layoutType: string,
    companyCode: string
  ): Promise<string> {
    const prefix = `${layoutType.toUpperCase()}_${companyCode}`;
    const existingCodes = await query<{ layout_code: string }>(
      `SELECT layout_code FROM layout_standards 
       WHERE layout_code LIKE $1`,
      [`${prefix}%`]
    );

    const maxNumber = existingCodes.reduce((max: number, item: any) => {
      const match = item.layout_code.match(/_(\d+)$/);
      if (match) {
        const number = parseInt(match[1], 10);
        return Math.max(max, number);
      }
      return max;
    }, 0);

    return `${prefix}_${String(maxNumber + 1).padStart(3, "0")}`;
  }

  /**
   * 데이터베이스 모델을 LayoutStandard 타입으로 변환
   */
  private mapToLayoutStandard(layout: any): LayoutStandard {
    return {
      layoutCode: layout.layout_code,
      layoutName: layout.layout_name,
      layoutNameEng: layout.layout_name_eng,
      description: layout.description,
      layoutType: layout.layout_type,
      category: layout.category,
      iconName: layout.icon_name,
      defaultSize: layout.default_size,
      layoutConfig: layout.layout_config,
      zonesConfig: layout.zones_config,
      previewImage: layout.preview_image,
      sortOrder: layout.sort_order,
      isActive: layout.is_active,
      isPublic: layout.is_public,
      companyCode: layout.company_code,
      createdDate: layout.created_date,
      createdBy: layout.created_by,
      updatedDate: layout.updated_date,
      updatedBy: layout.updated_by,
    };
  }
}

export const layoutService = new LayoutService();
