import { Controller, Route, Security, Tags, Path, Request, Response, Get, Query } from "tsoa";
import { AppDataSource } from "../database/data-source";
import HttpSuccess from "../interfaces/http-success";
import HttpStatusCode from "../interfaces/http-status";
import HttpError from "../interfaces/http-error";
import { ApiName } from "../entities/ApiName";
import { isPermissionRequest } from "../middlewares/authWebService";
import { RequestWithUserWebService } from "../middlewares/user";
import { OrgRevision } from "../entities/OrgRevision";
import { ApiHistory } from "../entities/ApiHistory";
import { SystemCode } from "./../interfaces/api-type";
@Route("api/v1/org/api-service")
@Tags("ApiKey")
@Security("webServiceAuth")
@Response(
  HttpStatusCode.INTERNAL_SERVER_ERROR,
  "เกิดข้อผิดพลาด ไม่สามารถแสดงรายการได้ กรุณาลองใหม่ในภายหลัง",
)
export class ApiWebServiceController extends Controller {
  private apiNameRepository = AppDataSource.getRepository(ApiName);
  private orgRevisionRepository = AppDataSource.getRepository(OrgRevision);
  private apiHistoryRepository = AppDataSource.getRepository(ApiHistory);

  /**
   * list fields by systems
   * @summary รายการ fields ตาม systems
   */
  @Get("/:system/:code")
  async listAttribute(
    @Request() request: RequestWithUserWebService,
    @Path("system")
    system: SystemCode,
    @Path("code") code: string,
    @Query("page") page: number = 1,
    @Query("pageSize") pageSize: number = 100,
  ): Promise<HttpSuccess | HttpError> {
    const apiName = await this.apiNameRepository.findOne({
      where: { code },
      select: ["id", "code", "methodApi", "system", "isActive"],
      relations: ["apiAttributes"],
      order: {
        apiAttributes: {
          ordering: "ASC",
        },
      },
    });

    if (!apiName || apiName.system != system || !apiName.isActive || apiName.methodApi != "GET") {
      throw new HttpError(HttpStatusCode.NOT_FOUND, "ไม่พบ API ที่ระบุ");
    }
    await isPermissionRequest(request, apiName.id);
    const offset = (page - 1) * pageSize;
    const propertyKey = apiName.apiAttributes.map((attr) => `${attr.tbName}.${attr.propertyKey}`);

    let tbMain: string = "";
    let condition: string = "1=1";
    if (system == "registry") {
      tbMain = "Profile";
    } else if (system == "registry_emp") {
      tbMain = "ProfileEmployee";
      condition = `ProfileEmployee.employeeClass = "PERM"`;
    } else if (system == "registry_temp") {
      tbMain = "ProfileEmployee";
      condition = `ProfileEmployee.employeeClass = "TEMP"`;
    } else if (system == "organization") {
      tbMain = "OrgRoot";
      const revision = await this.orgRevisionRepository.findOne({
        select: ["id"],
        where: { orgRevisionIsCurrent: true, orgRevisionIsDraft: false },
      });
      condition = `OrgRoot.orgRevisionId = "${revision?.id}"`;
    } else if (system == "position") {
      tbMain = "PosMaster";
      const revision = await this.orgRevisionRepository.findOne({
        select: ["id"],
        where: { orgRevisionIsCurrent: true, orgRevisionIsDraft: false },
      });
      condition = `PosMaster.orgRevisionId = "${revision?.id}"`;
    }

    const repo = AppDataSource.getRepository(tbMain);
    const metadata = repo.metadata;

    const relationMap: Record<string, string> = {};
    metadata.relations.forEach((rel) => {
      relationMap[rel.inverseEntityMetadata.name] = rel.propertyName;
    });

    // ดึงเฉพาะตารางรอง (ถ้าเลือกไว้)
    let propertyOtherKey: any[] = [];
    propertyOtherKey = [
      ...new Set(propertyKey.map((x) => x.split(".")[0]).filter((tb) => tb !== tbMain)),
    ];

    const queryBuilder = repo.createQueryBuilder(tbMain);

    // join กับตารารอง
    if (propertyOtherKey.length > 0) {
      propertyOtherKey.forEach((tb) => {
        const relationName = relationMap[tb];
        if (relationName) {
          queryBuilder.leftJoin(
            `${tbMain}.${relationName === "next_holder" ? "current_holder" : relationName}`, // เช็คว่าถ้าเป็น next_holder ให้ใช้ current_holder แทน
            tb,
          );
        }
      });
    }

    // // เพิ่ม Main.id เพราะจะใช้ pk ในการแมบและนับจำนวน
    // if (!propertyKey.includes(`${Main}.id`)) {
    //   propertyKey.push(`${Main}.id`);
    // }

    // add FK
    let pk: string = "";
    const primaryColumns = metadata.primaryColumns;
    primaryColumns.forEach((col) => {
      pk = col.propertyName;
      if (!propertyKey.includes(`${tbMain}.${pk}`)) {
        propertyKey.push(`${tbMain}.${pk}`);
      }
    });

    const [items, total] = await queryBuilder
      .select(propertyKey)
      .where(condition)
      .orderBy(propertyKey[0], "ASC")
      .skip(offset)
      .take(pageSize)
      .getManyAndCount();

    // ลบ Main.id
    // const results = items.map(({ id, ...x }) => x);
    // const results = items.map(({ pk, ...x }) => x);

    // const results = items.map(item => {
    //   primaryColumns.forEach(col => delete item[col.propertyName]);
    //   return item;
    // });

    // split object id ออกก่อน return
    const data = items.map((item) => {
      const { [pk]: removedPk, ...x } = item;
      return x;
    });

    // console.log("queryBuilder ===> ", queryBuilder.getQuery());

    // save api history after query success
    const history = {
      headerApi: JSON.stringify({
        host: request.headers.host,
        "x-api-key": request.headers["x-api-key"],
        connection: request.headers.connection,
        accept: request.headers.accept,
      }),
      tokenApi: Array.isArray(request.headers["x-api-key"])
        ? request.headers["x-api-key"][0] || ""
        : request.headers["x-api-key"] || "",
      requestApi: `${request.method} ${request.protocol}://${request.headers.host}${request.originalUrl || request.url}`,
      responseApi: "OK",
      ipApi: request.ip,
      codeApi: code,
      apiKeyId: request.user.id,
      apiNameId: apiName.id,
      createdFullName: request.user.name,
      lastUpdateFullName: request.user.name,
    };
    await this.apiHistoryRepository.save(history);

    // const results = data.map((item) => {
    //   const flattenedItem: any = {};

    //   // Extract nested object properties to top level
    //   Object.keys(item).forEach((key) => {
    //     const value = item[key];
    //     if (value && typeof value === "object") {
    //       // if (Array.isArray(value) && value.length === 1) {
    //       //   // If array has single item, extract it as object
    //       //   Object.assign(flattenedItem, value[0]);
    //       // } else
    //       if (!Array.isArray(value)) {
    //         // Merge nested object properties to top level
    //         Object.assign(flattenedItem, value);
    //       } else {
    //         // Keep arrays with multiple items or empty arrays as is
    //         flattenedItem[key] = value;
    //       }
    //     } else {
    //       // Keep primitive values as is
    //       flattenedItem[key] = value;
    //     }
    //   });

    //   return flattenedItem;
    // });
    return new HttpSuccess({ data: data, total });
  }
}
