import { Wheel } from "../models/spinWheelModel";
import { Spin } from "../models/SpinWheelHistoryModel";
import { pickWeightedSegment } from "../utils/pickWeightedSegment";
import User from "../models/users";

function withStatus(message: string, statusCode: number) {
  const error = new Error(message) as Error & { statusCode?: number };
  error.statusCode = statusCode;
  return error;
}

type WheelSegmentInput = {
  label: string;
  prizeType: "points" | "voucher" | "gift" | "retry" | "none";
  prizeValue?: number;
  weight: number;
  color?: string;
};

type CreateWheelInput = {
  name: string;
  segments: WheelSegmentInput[];
  isActive?: boolean;
};

type UpdateWheelInput = {
  name?: string;
  segments?: WheelSegmentInput[];
  isActive?: boolean;
};

//----------------Admin Service----------------

export async function createWheel(payload: CreateWheelInput) {
  const segments = payload.segments.map((segment) => ({
    ...segment,
    prizeValue: segment.prizeValue ?? 0,
  }));

  const totalWeight = segments.reduce(
    (sum, segment) => sum + segment.weight,
    0,
  );

  if (totalWeight <= 0) {
    throw withStatus("Total segment weight must be greater than 0", 400);
  }

  if (payload.isActive !== false) {
    await Wheel.updateMany({ isActive: true }, { $set: { isActive: false } });
  }

  return Wheel.create({
    name: payload.name,
    isActive: payload.isActive !== false,
    segments,
  });
}

export async function listWheels() {
  return Wheel.find().sort({ createdAt: -1 });
}

export async function updateWheel(wheelId: string, payload: UpdateWheelInput) {
  const update: Record<string, unknown> = {};

  if (typeof payload.name === "string" && payload.name.trim()) {
    update.name = payload.name.trim();
  }

  if (Array.isArray(payload.segments)) {
    if (payload.segments.length === 0) {
      throw withStatus("segments must not be empty", 400);
    }

    const segments = payload.segments.map((segment) => ({
      ...segment,
      prizeValue: segment.prizeValue ?? 0,
    }));

    const totalWeight = segments.reduce(
      (sum, segment) => sum + segment.weight,
      0,
    );

    if (totalWeight <= 0) {
      throw withStatus("Total segment weight must be greater than 0", 400);
    }

    update.segments = segments;
  }

  if (typeof payload.isActive === "boolean") {
    update.isActive = payload.isActive;
  }

  if (Object.keys(update).length === 0) {
    throw withStatus("No valid fields provided for update", 400);
  }

  if (payload.isActive === true) {
    await Wheel.updateMany(
      { _id: { $ne: wheelId }, isActive: true },
      { $set: { isActive: false } },
    );
  }

  const wheel = await Wheel.findByIdAndUpdate(wheelId, update, { new: true });

  if (!wheel) {
    throw withStatus("Wheel not found", 404);
  }

  return wheel;
}

export async function activateWheel(wheelId: string) {
  const wheel = await Wheel.findById(wheelId);

  if (!wheel) {
    throw withStatus("Wheel not found", 404);
  }

  await Wheel.updateMany(
    { _id: { $ne: wheelId }, isActive: true },
    { $set: { isActive: false } },
  );

  wheel.isActive = true;
  await wheel.save();
  return wheel;
}

export async function deactivateWheel(wheelId: string) {
  const wheel = await Wheel.findById(wheelId);

  if (!wheel) {
    throw withStatus("Wheel not found", 404);
  }

  wheel.isActive = false;
  await wheel.save();
  return wheel;
}

export async function deleteWheel(wheelId: string) {
  const wheel = await Wheel.findById(wheelId);

  if (!wheel) {
    throw withStatus("Wheel not found", 404);
  }

  if (wheel.isActive) {
    throw withStatus("Cannot delete an active wheel. Deactivate it first", 400);
  }

  await Wheel.findByIdAndDelete(wheelId);
  return { wheelId };
}




//----------------User Service----------------
export async function getWheel() {
  const wheel = await Wheel.findOne({ isActive: true });

  if (!wheel) {
    throw withStatus("No active wheel found", 404);
  }

  return wheel;
}

export async function getWheelEligibility(userId: string) {
  const today = new Date();
  today.setHours(0, 0, 0, 0);

  const spinToday = await Spin.findOne({
    userId,
    createdAt: {
      $gte: today,
    },
  });

  return {
    canSpin: !spinToday,
    remainingSpins: spinToday ? 0 : 1,
    nextSpinAt: spinToday
      ? new Date(today.getTime() + 24 * 60 * 60 * 1000)
      : null,
  };
  // return {
  //   canSpin: true,
  //   remainingSpins: Infinity,
  //   nextSpinAt: null,
  // };
}

export async function spinWheel(userId: string) {
  // Check eligibility
  const eligibility = await getWheelEligibility(userId);

  if (!eligibility.canSpin) {
    throw withStatus("No spins remaining", 400);
  }

  // Get active wheel
  const wheel = await Wheel.findOne({ isActive: true });

  if (!wheel) {
    throw withStatus("No active wheel found", 404);
  }

  // Pick a segment
  const winningSegment = pickWeightedSegment(wheel.segments);

  // Save history
  await Spin.create({
    userId,
    wheelId: wheel._id,
    segmentId: winningSegment._id,
    prizeType: winningSegment.prizeType,
    prizeValue: winningSegment.prizeValue,
  });

  if (
    winningSegment.prizeType === "points" &&
    typeof winningSegment.prizeValue === "number" &&
    winningSegment.prizeValue > 0
  ) {
    await User.findByIdAndUpdate(userId, {
      $inc: {
        earned_points: winningSegment.prizeValue,
        spin_wheel_points: winningSegment.prizeValue,
      },
    });
  }

  return {
    wheelId: wheel._id,
    segment: winningSegment,
  };
}

export async function getSpinHistory(userId: string) {
  return Spin.find({ userId }).sort({ createdAt: -1 }).limit(20);
}
