import { logError } from "@/lib/logger";
import { error as errorResponse, success } from "@/lib/response";
import { PointsTransferService } from "@/v1/services/admin/points-transfer/points-transfer.service";
import type { Context } from "hono";
import type { Kysely } from "kysely";
import type { DB } from "@/internal/datastore/db";

const getAdminUserByEmail = async (db: Kysely<DB>, email: string) => {
  return await db
    .selectFrom("console_users")
    .select(["id", "email", "is_super_admin", "viewpoint_account_id"])
    .where("email", "=", email)
    .where("is_deleted", "=", false)
    .executeTakeFirst();
};

/**
 * Resolves the caller and the manager scope to apply.
 *
 * `ownerId` is undefined for super admins (full access) and the caller's own id
 * for everyone else, which limits them to the managers they created. It is
 * never silently undefined for a non-super-admin: if we cannot identify the
 * caller, `userId` comes back undefined and the handler must reject.
 */
const resolveActor = async (c: Context) => {
  const datastore = c.get("datastore") as Kysely<DB>;
  const email = c.get("adminEmail") as string | undefined;
  const user = email ? await getAdminUserByEmail(datastore, email) : undefined;

  const userId = user?.id ?? (c.get("consoleUserId") as string | undefined);
  const isSuperAdmin =
    user?.is_super_admin === true || c.get("isAdminConsoleSuperAdmin") === true;

  return { datastore, user, userId, isSuperAdmin, ownerId: isSuperAdmin ? undefined : userId };
};

export const getHoldingBalancesHandler = async (c: Context) => {
  try {
    const datastore = c.get("datastore");
    const service = new PointsTransferService(datastore);
    const balances = await service.getHoldingBalances();
    return success(c, balances, "Holding balances retrieved successfully");
  } catch (error) {
    logError("getHoldingBalancesHandler error", { error });
    return errorResponse(c, "Failed to retrieve holding balances", 500);
  }
};

export const syncHoldingBalancesHandler = async (c: Context) => {
  try {
    const datastore = c.get("datastore");
    const service = new PointsTransferService(datastore);
    // ViewPoint's entitlements endpoint takes ~50s per holding account and the
    // two must run sequentially, so a full sync is ~105s. We hold the response
    // open and return the fresh balances the moment the run settles — that way
    // the client updates and drops its loader immediately instead of waiting
    // for the next poll tick. If the wait caps out, `timedOut` tells the client
    // to fall back to polling; the sync itself carries on regardless.
    const result = await service.syncHoldingBalancesAndWait();
    return success(
      c,
      result,
      result.balances.syncError
        ? `Holding balance sync finished with errors: ${result.balances.syncError}`
        : result.timedOut
          ? "Holding balance sync is still running"
          : "Holding balances synced successfully",
    );
  } catch (error) {
    logError("syncHoldingBalancesHandler error", { error });
    return errorResponse(c, "Failed to start holding balance sync", 500);
  }
};

export const getManagersHandler = async (c: Context) => {
  try {
    const { datastore, userId, isSuperAdmin, ownerId } = await resolveActor(c);
    if (!isSuperAdmin && !userId) return errorResponse(c, "Unauthorized", 401);

    const service = new PointsTransferService(datastore);
    const managers = await service.getManagers(true, ownerId);
    return success(c, managers, "Managers retrieved successfully");
  } catch (error) {
    logError("getManagersHandler error", { error });
    return errorResponse(c, "Failed to retrieve managers", 500);
  }
};

export const getTransactionsHandler = async (c: Context) => {
  try {
    const limit = Number(c.req.query("limit")) || 3;
    const offset = Number(c.req.query("offset")) || 0;
    const search = c.req.query("search") || undefined;

    const { datastore, userId, isSuperAdmin, ownerId } = await resolveActor(c);
    if (!isSuperAdmin && !userId) return errorResponse(c, "Unauthorized", 401);

    const service = new PointsTransferService(datastore);
    const transactions = await service.getTransactions(limit, offset, search, ownerId);
    return success(c, transactions, "Transactions retrieved successfully");
  } catch (error) {
    logError("getTransactionsHandler error", { error });
    return errorResponse(c, "Failed to retrieve transactions", 500);
  }
};

export const transferPointsHandler = async (c: Context) => {
  try {
    const body = await c.req.json();

    if (!body.targetManagerId || !body.points || body.points <= 0) {
      return errorResponse(c, "Invalid transfer payload", 400);
    }

    const { datastore, userId, isSuperAdmin, ownerId } = await resolveActor(c);
    if (!isSuperAdmin && !userId) return errorResponse(c, "Unauthorized", 401);

    const service = new PointsTransferService(datastore);
    const result = await service.transfer(
      body,
      userId ?? "00000000-0000-0000-0000-000000000000",
      ownerId,
    );

    return success(c, result, "Points transferred successfully");
  } catch (error: any) {
    logError("transferPointsHandler error", { error });
    return errorResponse(c, error.message || "Failed to transfer points", 500);
  }
};

// --- PHASE 2 HANDLERS ---

export const searchViewPointByEmailHandler = async (c: Context) => {
  try {
    const email = c.req.query("email");
    const memberNo = c.req.query("memberNo");

    const datastore = c.get("datastore");
    const service = new PointsTransferService(datastore);

    if (memberNo) {
      const results = await service.getMemberByNumber(memberNo);
      return success(c, results, "ViewPoint member retrieved successfully");
    }

    if (!email) return errorResponse(c, "Email or Member Number is required", 400);

    const results = await service.searchViewPointByEmail(email);
    return success(c, results, "ViewPoint members retrieved successfully");
  } catch (error: any) {
    logError("searchViewPointByEmailHandler error", { error });
    return errorResponse(c, "Failed to search ViewPoint", 500);
  }
};

export const mapManagerHandler = async (c: Context) => {
  try {
    const managerId = c.req.param("id");
    const body = await c.req.json();
    
    if (!managerId) return errorResponse(c, "Manager ID is required", 400);

    if (!body.accountId || !body.contactId || !body.club) {
      return errorResponse(c, "Invalid mapping payload", 400);
    }

    const { datastore, userId, isSuperAdmin, ownerId } = await resolveActor(c);
    if (!isSuperAdmin && !userId) return errorResponse(c, "Unauthorized", 401);

    const service = new PointsTransferService(datastore);
    const result = await service.mapManager(managerId, body, userId, ownerId);

    return success(c, result, "Manager mapped successfully");
  } catch (error: any) {
    logError("mapManagerHandler error", { error });
    return errorResponse(c, error.message || "Failed to map manager", 500);
  }
};

export const getManagerBalancesHandler = async (c: Context) => {
  try {
    const email = c.get("adminEmail");
    if (!email) return errorResponse(c, "Unauthorized", 401);

    const datastore = c.get("datastore");
    const user = await getAdminUserByEmail(datastore, email);
    if (!user) return errorResponse(c, "User not found", 404);

    const service = new PointsTransferService(datastore);
    const balances = await service.getManagerBalances(user.id);
    return success(c, balances, "Manager balances retrieved successfully");
  } catch (error) {
    logError("getManagerBalancesHandler error", { error });
    return errorResponse(c, "Failed to retrieve manager balances", 500);
  }
};

export const getManagerTransactionsHandler = async (c: Context) => {
  try {
    const email = c.get("adminEmail");
    if (!email) return errorResponse(c, "Unauthorized", 401);

    const limit = Number(c.req.query("limit")) || 5;
    const offset = Number(c.req.query("offset")) || 0;
    const search = c.req.query("search");
    
    const datastore = c.get("datastore");
    const user = await getAdminUserByEmail(datastore, email);
    if (!user) return errorResponse(c, "User not found", 404);

    const service = new PointsTransferService(datastore);
    const result = await service.getManagerTransactions(user.id, limit, offset, search);
    return success(c, result, "Transactions retrieved successfully");
  } catch (error) {
    logError("getManagerTransactionsHandler error", { error });
    return errorResponse(c, "Failed to retrieve transactions", 500);
  }
};

export const getManagerEntitlementsHandler = async (c: Context) => {
  try {
    const email = c.get("adminEmail");
    if (!email) return errorResponse(c, "Unauthorized", 401);

    const datastore = c.get("datastore");
    const user = await getAdminUserByEmail(datastore, email);
    if (!user) return errorResponse(c, "User not found", 404);

    const service = new PointsTransferService(datastore);
    const entitlements = await service.getManagerEntitlements(user.id);
    return success(c, entitlements, "Entitlements retrieved successfully");
  } catch (error: any) {
    logError("getManagerEntitlementsHandler error", { error });
    return errorResponse(c, error.message || "Failed to retrieve entitlements", 500);
  }
};

export const transferToMemberHandler = async (c: Context) => {
  try {
    const body = await c.req.json();
    const email = c.get("adminEmail");
    if (!email) return errorResponse(c, "Unauthorized", 401);
    
    if (!body.targetAccountId || !body.targetContactId || !body.points || body.points <= 0) {
      return errorResponse(c, "Invalid transfer payload.", 400);
    }
    
    const datastore = c.get("datastore");
    const user = await getAdminUserByEmail(datastore, email);
    if (!user) return errorResponse(c, "User not found", 404);

    if (!user.viewpoint_account_id) {
      return errorResponse(c, "Bad Request: Only mapped managers can transfer points", 400);
    }

    const service = new PointsTransferService(datastore);
    const result = await service.transferToMember(body, user.id);
    
    return success(c, result, "Points transferred successfully");
  } catch (error: any) {
    logError("transferToMemberHandler error", { error });
    return errorResponse(c, error.message || "Failed to transfer points", 500);
  }
};

export const updateManagerHandler = async (c: Context) => {
  try {
    const managerId = c.req.param("id");
    const body = await c.req.json();
    
    if (!managerId) return errorResponse(c, "Manager ID is required", 400);

    const { datastore, userId, isSuperAdmin, ownerId } = await resolveActor(c);
    if (!isSuperAdmin && !userId) return errorResponse(c, "Unauthorized", 401);

    const service = new PointsTransferService(datastore);
    const result = await service.updateManager(managerId, body, ownerId);

    return success(c, result, "Manager profile updated successfully");
  } catch (error: any) {
    logError("updateManagerHandler error", { error });
    return errorResponse(c, error.message || "Failed to update manager profile", 500);
  }
};

export const deleteManagerHandler = async (c: Context) => {
  try {
    const managerId = c.req.param("id");
    if (!managerId) return errorResponse(c, "Manager ID is required", 400);

    const { datastore, userId, isSuperAdmin, ownerId } = await resolveActor(c);
    if (!isSuperAdmin && !userId) return errorResponse(c, "Unauthorized", 401);

    const service = new PointsTransferService(datastore);
    const result = await service.deleteManager(managerId, ownerId);

    return success(c, result, "Manager deactivated successfully");
  } catch (error: any) {
    logError("deleteManagerHandler error", { error });
    return errorResponse(c, error.message || "Failed to deactivate manager", 500);
  }
};

export const restoreManagerHandler = async (c: Context) => {
  try {
    const managerId = c.req.param("id");
    if (!managerId) return errorResponse(c, "Manager ID is required", 400);

    const { datastore, userId, isSuperAdmin, ownerId } = await resolveActor(c);
    if (!isSuperAdmin && !userId) return errorResponse(c, "Unauthorized", 401);

    const service = new PointsTransferService(datastore);
    const result = await service.restoreManager(managerId, ownerId);

    return success(c, result, "Manager restored successfully");
  } catch (error: any) {
    logError("restoreManagerHandler error", { error });
    return errorResponse(c, error.message || "Failed to restore manager", 500);
  }
};

export const getManagerHistoryHandler = async (c: Context) => {
  try {
    const managerId = c.req.param("id");
    if (!managerId) return errorResponse(c, "Manager ID is required", 400);

    const { datastore, userId, isSuperAdmin, ownerId } = await resolveActor(c);
    if (!isSuperAdmin && !userId) return errorResponse(c, "Unauthorized", 401);

    const service = new PointsTransferService(datastore);
    const history = await service.getManagerHistory(managerId, ownerId);

    return success(c, history, "Manager history retrieved successfully");
  } catch (error: any) {
    logError("getManagerHistoryHandler error", { error });
    return errorResponse(c, error.message || "Failed to retrieve manager history", 500);
  }
};
