import { desc, eq, type Database } from "@pkg/db";
import { customerInfo, order } from "@pkg/db/schema";
import { getError, Logger } from "@pkg/logger";
import { ERROR_CODES, type Result } from "@pkg/result";
import {
  customerInfoModel,
  type CreateCustomerInfoPayload,
  type CustomerInfoModel,
  type UpdateCustomerInfoPayload,
} from "./data";

export class CustomerInfoRepository {
  private db: Database;

  constructor(db: Database) {
    this.db = db;
  }

  async getAllCustomerInfo(): Promise<Result<CustomerInfoModel[]>> {
    try {
      const results = await this.db.query.customerInfo.findMany({
        orderBy: [desc(customerInfo.createdAt)],
      });
      const out = [] as CustomerInfoModel[];
      for (const result of results) {
        const parsed = customerInfoModel.safeParse(result);
        if (!parsed.success) {
          Logger.error("Failed to parse customer info");
          Logger.error(parsed.error);
          continue;
        }
        out.push(parsed.data);
      }
      return { data: out };
    } catch (e) {
      return {
        error: getError(
          {
            code: ERROR_CODES.DATABASE_ERROR,
            message: "Failed to fetch customer information",
            detail:
              "An error occurred while retrieving customer information from the database",
            userHint: "Please try refreshing the page",
            actionable: false,
          },
          e,
        ),
      };
    }
  }

  async getCustomerInfoById(id: number): Promise<Result<CustomerInfoModel>> {
    try {
      const result = await this.db.query.customerInfo.findFirst({
        where: eq(customerInfo.id, id),
      });

      if (!result) {
        return {
          error: getError({
            code: ERROR_CODES.NOT_FOUND_ERROR,
            message: "Customer information not found",
            detail: "No customer information exists with the provided ID",
            userHint: "Please check the customer ID and try again",
            actionable: true,
          }),
        };
      }

      const parsed = customerInfoModel.safeParse(result);
      if (!parsed.success) {
        Logger.error("Failed to parse customer info", result);
        return {
          error: getError({
            code: ERROR_CODES.INTERNAL_SERVER_ERROR,
            message: "Failed to parse customer information",
            userHint: "Please try again",
            detail: "Failed to parse customer information",
          }),
        };
      }
      return { data: parsed.data };
    } catch (e) {
      return {
        error: getError(
          {
            code: ERROR_CODES.DATABASE_ERROR,
            message: "Failed to fetch customer information",
            detail:
              "An error occurred while retrieving the customer information from the database",
            userHint: "Please try refreshing the page",
            actionable: false,
          },
          e,
        ),
      };
    }
  }

  async getCustomerInfoByOrderId(
    oid: number,
  ): Promise<Result<CustomerInfoModel>> {
    try {
      const result = await this.db.query.order.findFirst({
        where: eq(order.id, oid),
        columns: { id: true },
        with: { customerInfo: true },
      });
      const parsed = customerInfoModel.safeParse(result?.customerInfo);
      if (!parsed.success) {
        Logger.error("Failed to parse customer info", result);
        return {
          error: getError({
            code: ERROR_CODES.INTERNAL_SERVER_ERROR,
            message: "Failed to parse customer information",
            userHint: "Please try again",
            detail: "Failed to parse customer information",
          }),
        };
      }
      return { data: parsed.data };
    } catch (e) {
      return {
        error: getError(
          {
            code: ERROR_CODES.DATABASE_ERROR,
            message: "Failed to fetch customer information",
            detail:
              "An error occurred while retrieving the customer information from the database",
            userHint: "Please try refreshing the page",
            actionable: false,
          },
          e,
        ),
      };
    }
  }

  async createCustomerInfo(
    payload: CreateCustomerInfoPayload,
  ): Promise<Result<number>> {
    try {
      const result = await this.db
        .insert(customerInfo)
        .values({
          firstName: payload.firstName,
          middleName: payload.middleName || "",
          lastName: payload.lastName,
          email: payload.email,
          phoneCountryCode: payload.phoneCountryCode,
          phoneNumber: payload.phoneNumber,
          country: payload.country,
          state: payload.state,
          city: payload.city,
          zipCode: payload.zipCode,
          address: payload.address,
          address2: payload.address2 || null,
        })
        .returning({ id: customerInfo.id })
        .execute();

      if (!result || result.length === 0) {
        throw new Error("Failed to create customer info record");
      }

      return { data: result[0].id };
    } catch (e) {
      return {
        error: getError(
          {
            code: ERROR_CODES.DATABASE_ERROR,
            message: "Failed to create customer information",
            detail: "An error occurred while creating the customer information",
            userHint: "Please try again",
            actionable: false,
          },
          e,
        ),
      };
    }
  }

  async updateCustomerInfo(
    payload: UpdateCustomerInfoPayload,
  ): Promise<Result<boolean>> {
    try {
      if (!payload.id) {
        return {
          error: getError({
            code: ERROR_CODES.VALIDATION_ERROR,
            message: "Invalid customer ID",
            detail: "No customer ID was provided for the update operation",
            userHint: "Please provide a valid customer ID",
            actionable: true,
          }),
        };
      }

      // Check if customer info exists
      const existing = await this.db.query.customerInfo.findFirst({
        where: eq(customerInfo.id, payload.id),
      });

      if (!existing) {
        return {
          error: getError({
            code: ERROR_CODES.NOT_FOUND_ERROR,
            message: "Customer information not found",
            detail: "No customer information exists with the provided ID",
            userHint: "Please check the customer ID and try again",
            actionable: true,
          }),
        };
      }

      // Build the update object with only the fields that are provided
      const updateValues: Record<string, any> = {};

      if (payload.firstName !== undefined)
        updateValues.firstName = payload.firstName;
      if (payload.middleName !== undefined)
        updateValues.middleName = payload.middleName;
      if (payload.lastName !== undefined)
        updateValues.lastName = payload.lastName;
      if (payload.email !== undefined) updateValues.email = payload.email;
      if (payload.phoneCountryCode !== undefined)
        updateValues.phoneCountryCode = payload.phoneCountryCode;
      if (payload.phoneNumber !== undefined)
        updateValues.phoneNumber = payload.phoneNumber;
      if (payload.country !== undefined) updateValues.country = payload.country;
      if (payload.state !== undefined) updateValues.state = payload.state;
      if (payload.city !== undefined) updateValues.city = payload.city;
      if (payload.zipCode !== undefined) updateValues.zipCode = payload.zipCode;
      if (payload.address !== undefined) updateValues.address = payload.address;
      if (payload.address2 !== undefined)
        updateValues.address2 = payload.address2;
      updateValues.updatedAt = new Date();

      await this.db
        .update(customerInfo)
        .set(updateValues)
        .where(eq(customerInfo.id, payload.id))
        .execute();

      return { data: true };
    } catch (e) {
      return {
        error: getError(
          {
            code: ERROR_CODES.DATABASE_ERROR,
            message: "Failed to update customer information",
            detail: "An error occurred while updating the customer information",
            userHint: "Please try again",
            actionable: false,
          },
          e,
        ),
      };
    }
  }

  async deleteCustomerInfo(id: number): Promise<Result<boolean>> {
    try {
      // Check if customer info exists
      const existing = await this.db.query.customerInfo.findFirst({
        where: eq(customerInfo.id, id),
      });

      if (!existing) {
        return {
          error: getError({
            code: ERROR_CODES.NOT_FOUND_ERROR,
            message: "Customer information not found",
            detail: "No customer information exists with the provided ID",
            userHint: "Please check the customer ID and try again",
            actionable: true,
          }),
        };
      }

      await this.db
        .delete(customerInfo)
        .where(eq(customerInfo.id, id))
        .execute();

      return { data: true };
    } catch (e) {
      return {
        error: getError(
          {
            code: ERROR_CODES.DATABASE_ERROR,
            message: "Failed to delete customer information",
            detail: "An error occurred while deleting the customer information",
            userHint: "Please try again",
            actionable: false,
          },
          e,
        ),
      };
    }
  }
}
