<template>
  <div v-if="bettingDetailInfo !== undefined && info !== undefined">
    <div class="bg2">
      {{ info.name }}
      <span v-if="info.finished">(종료)</span>
      <span v-else-if="(yearMonth ?? 0) <= info.closeYearMonth"
        >({{ parseYearMonth(info.closeYearMonth)[0] }}년 {{ parseYearMonth(info.closeYearMonth)[1] }}월까지)</span
      >
      <span v-else>(베팅 마감)</span>
      (총액: {{ bettingAmount.toLocaleString() }})
    </div>

    <div class="row bettingCandidates gx-1 gy-1">
      <div v-for="(candidate, idx) in info.candidates" :key="idx" class="col-4 col-lg-2" @click="toggleCandidate(parseInt(idx))">
        <div
          :class="[
            'bettingCandidate',
            pickedBetType.has(parseInt(idx)) ? 'picked' : undefined,
            info.finished && winner.has(parseInt(idx)) ? 'picked' : undefined,
          ]"
        >
          <div class="title bg1">
            {{ candidate.title }}
          </div>
          <!-- eslint-disable-next-line vue/no-v-html -->
          <div v-if="candidate.isHtml" class="info" v-html="candidate.info" />
          <div v-else class="info">{{ candidate.info }}</div>
          <div class="pickRate">선택율: {{ (((partialBet.get(parseInt(idx)) ?? 0) / pureBettingAmount) * 100).toFixed(1) }}%</div>
        </div>
      </div>
    </div>
    <div v-if="!info.finished && (yearMonth ?? 0) <= info.closeYearMonth" class="row gx-0">
      <div class="col-6 col-lg-3 align-self-center">
        잔여 {{ info.reqInheritancePoint ? "포인트" : "금" }} : {{ bettingDetailInfo.remainPoint.toLocaleString() }}
      </div>
      <div class="col-6 col-lg-3 align-self-center">
        사용 포인트: {{ sum(Array.from(myBettings.values())).toLocaleString() }}
      </div>
      <div class="col-6 col-lg-3 align-self-center">대상: {{ getTypeStr(pickedBetTypeKey) }}</div>
      <div class="col-4 col-lg-2 d-grid">
        <!-- eslint-disable-next-line vue/max-attributes-per-line -->
        <b-form-input v-model.number="betPoint" class="d-grid" type="number" :min="10" :max="1000" :step="10" />
      </div>
      <div class="col-2 col-lg-1 d-grid">
        <b-button class="d-grid" @click="submitBet"> 베팅 </b-button>
      </div>
    </div>

    <div>
      <div class="bg2">배당 순위</div>
      <div
        class="row"
        :style="{
          borderBottom: 'gray solid 1px',
        }"
      >
        <div class="col-5 text-center">대상</div>
        <div class="col-2 text-center">베팅액</div>
        <div class="col-3 text-center">내 베팅</div>
        <div class="col-2 text-center">
          {{ info.finished ? "배율" : "기대 배율" }}
        </div>
      </div>
      <template v-if="info.finished">
        <div v-for="[betType, amount] of detailBet" :key="betType" class="row">
          <template v-for="[matchPoint, color] of [calcMatchPointWithColor(betType)]" :key="matchPoint">
            <div
              class="col-5"
              :style="{
                fontWeight: myBettings.has(betType) ? 'bold' : undefined,
                color: color,
              }"
            >
              {{ getTypeStr(betType) }}
            </div>
            <div class="col-2 text-end">
              {{ amount.toLocaleString() }}
            </div>
            <div v-if="myBettings.has(betType)" class="col-3 text-center">
              <template v-for="subPoint of [myBettings.get(betType) ?? 0]">
                ({{ subPoint.toLocaleString() }} ->
                {{
                  calculatedReward[matchPoint] == 0
                    ? 0
                    : ((subPoint * calculatedReward[matchPoint]) / (calculatedSubAmount.get(matchPoint) ?? 1))
                        .toFixed(1)
                        .toLocaleString()
                }})
              </template>
            </div>
            <div v-else class="col-3 text-center" />
            <div class="col-2 text-end">
              {{
                (calculatedReward[matchPoint] == 0
                  ? 0
                  : calculatedReward[matchPoint] / (calculatedSubAmount.get(matchPoint) ?? 1)
                )
                  .toFixed(1)
                  .toLocaleString()
              }}배
            </div>
          </template>
        </div>
      </template>
      <template v-else>
        <div v-for="[betType, amount] of detailBet" :key="betType" class="row">
          <div
            class="col-5"
            :style="{
              fontWeight: myBettings.has(betType) ? 'bold' : undefined,
            }"
          >
            {{ getTypeStr(betType) }}
          </div>
          <div class="col-2 text-end">
            {{ amount.toLocaleString() }}
          </div>
          <div v-if="myBettings.has(betType)" class="col-3 text-center">
            <template v-for="subPoint of [myBettings.get(betType) ?? 0]">
              ({{ subPoint.toLocaleString() }} ->
              {{ ((subPoint * maxBettingReward) / amount).toFixed(1).toLocaleString() }})
            </template>
          </div>
          <div v-else class="col-3 text-center" />
          <div class="col-2 text-end">{{ (maxBettingReward / amount).toFixed(1).toLocaleString() }}배</div>
        </div>
      </template>
    </div>
  </div>
</template>

<script setup lang="ts">
import type { ToastType } from "@/defs";
import type { BettingDetailResponse, BettingInfo } from "@/defs/API/Betting";
import { SammoAPI } from "@/SammoAPI";
import { joinYearMonth } from "@/util/joinYearMonth";
import { parseYearMonth } from "@/util/parseYearMonth";
import { isString, range, sum } from "lodash-es";
import { ref, type PropType, watch } from "vue";

const props = defineProps({
  bettingID: {
    type: Number as PropType<number>,
    required: true,
  },
});

const emit = defineEmits<{
  (event: "reqToast", content: ToastType): void;
}>();

const year = ref<number>(0);
const month = ref<number>(0);
const yearMonth = ref<number>(0);

const bettingDetailInfo = ref<BettingDetailResponse>();
const info = ref<BettingInfo>();

const bettingAmount = ref<number>(0);
const maxBettingReward = ref<number>(1);
const pureBettingAmount = ref<number>(0);

const partialBet = ref(new Map<number, number>());
const detailBet = ref<[string, number][]>([]);

const typeMap = ref(new Map<string, string>());
function getTypeStr(type: string): string {
  const typeResult = typeMap.value.get(type);
  if (typeResult !== undefined) {
    return typeResult;
  }
  const bettingSubTypes = JSON.parse(type) as number[];
  if (bettingSubTypes[0] < -1) {
    return "Invalid";
  }

  const textBettingType = bettingSubTypes
    .map((idx) => {
      return bettingDetailInfo.value?.bettingInfo.candidates[idx].title;
    })
    .join(", ");
  typeMap.value.set(type, textBettingType);
  return textBettingType;
}

const pickedBetType = ref(new Set<number>());
const pickedBetTypeKey = ref("[]");

const betPoint = ref(0);
const myBettings = ref(new Map<string, number>());

const winner = ref(new Set<number>());
const calculatedReward = ref<number[]>([]);
const calculatedSubAmount = ref(new Map<number, number>());

function calcMatchPointWithColor(type: string): [number, "green" | "yellow" | "red" | undefined] {
  if (!info.value?.finished) {
    return [0, undefined];
  }
  const bettingSubTypes = JSON.parse(type) as number[];
  if (bettingSubTypes[0] < -1) {
    return [0, undefined];
  }

  let matchPoint = 0;
  for (const subType of bettingSubTypes) {
    if (winner.value.has(subType)) {
      matchPoint += 1;
    }
  }

  if (info.value.isExclusive) {
    if (matchPoint == info.value.selectCnt) {
      return [matchPoint, "green"];
    } else {
      return [matchPoint, "red"];
    }
  }

  let color: "green" | "red" | "yellow" = "green";
  if (matchPoint == 0) {
    color = "red";
  } else if (matchPoint < info.value.selectCnt) {
    color = "yellow";
  }
  return [matchPoint, color];
}

function calcReward() {
  if (info.value === undefined || bettingDetailInfo.value === undefined) {
    throw "no info";
  }
  const selectCnt = info.value.selectCnt;
  const rewardAmount = new Array<number>(selectCnt).fill(0);

  const subAmount = new Map<number, number>();
  for (const [bettingTypeStr, amount] of bettingDetailInfo.value.bettingDetail) {
    if (amount == 0) {
      continue;
    }
    const [matchPoint] = calcMatchPointWithColor(bettingTypeStr);
    subAmount.set(matchPoint, (subAmount.get(matchPoint) ?? 0) + amount);
  }
  calculatedSubAmount.value = subAmount;

  if (selectCnt == 1) {
    rewardAmount[selectCnt - 1] = bettingAmount.value;
    calculatedReward.value = rewardAmount;
    return;
  }

  if (info.value.isExclusive) {
    rewardAmount[selectCnt - 1] = bettingAmount.value;
    calculatedReward.value = rewardAmount;
    return;
  }

  let remainRewardAmount = bettingAmount.value;
  let accumulatedRewardAmount = 0;
  let givenRewardAmount = bettingAmount.value;

  for (const matchPoint of range(selectCnt, 0, -1)) {
    givenRewardAmount /= 2;
    accumulatedRewardAmount += givenRewardAmount;
    if (!subAmount.has(matchPoint)) {
      continue;
    }
    rewardAmount[matchPoint] = accumulatedRewardAmount;
    remainRewardAmount -= accumulatedRewardAmount;
    accumulatedRewardAmount = 0;
  }

  //남은 상금은 '당첨자'에게 몰아준다.
  //당첨자가 아무도 없다면, 0개 맞춘 그룹에게 돌아간다.
  for (const matchPoint of range(selectCnt, -1, -1)) {
    if (!subAmount.has(matchPoint)) {
      continue;
    }
    rewardAmount[matchPoint] += remainRewardAmount;
    break;
  }

  calculatedReward.value = rewardAmount;
}

async function loadBetting(bettingID: number) {
  try {
    const result = await SammoAPI.Betting.GetBettingDetail[bettingID]();
    year.value = result.year;
    month.value = result.month;
    yearMonth.value = joinYearMonth(result.year, result.month);
    bettingDetailInfo.value = result;
    info.value = result.bettingInfo;

    typeMap.value.clear();
    partialBet.value.clear();

    const betSort = new Map<string, number>();

    let _bettingAmount = 0;
    let adminBettingAmount = 0;
    for (const [bettingType, amount] of result.bettingDetail) {
      console.log(amount, typeof amount);
      let userBet = true;
      const bettingSubTypes = JSON.parse(bettingType) as number[];
      for (const bettingSubType of bettingSubTypes) {
        if (bettingSubType < 0) {
          userBet = false;
          continue;
        }
        const oldValue = partialBet.value.get(bettingSubType) ?? 0;
        partialBet.value.set(bettingSubType, oldValue + amount);
      }

      if (userBet) {
        const oldValue = betSort.get(bettingType) ?? 0;
        betSort.set(bettingType, oldValue + amount);
      }

      _bettingAmount += amount;
      if (!userBet) {
        adminBettingAmount += amount;
      }
    }
    console.log(_bettingAmount);
    bettingAmount.value = _bettingAmount;
    pureBettingAmount.value = _bettingAmount - adminBettingAmount;
    if (info.value.isExclusive || info.value.selectCnt == 1) {
      maxBettingReward.value = _bettingAmount;
    } else {
      maxBettingReward.value = _bettingAmount / 2;
    }

    detailBet.value = Array.from(betSort.entries());
    detailBet.value.sort(([, lhsVal], [, rhsVal]) => {
      return rhsVal - lhsVal;
    });

    pickedBetType.value.clear();
    pickedBetTypeKey.value = "[]";
    myBettings.value.clear();

    if (result.bettingInfo.winner) {
      winner.value = new Set(result.bettingInfo.winner);
    } else {
      winner.value.clear();
    }

    for (const [betType, amount] of result.myBetting) {
      myBettings.value.set(betType, amount);
    }
    calcReward();
  } catch (e) {
    if (isString(e)) {
      emit("reqToast", {
        content: {
          title: "에러",
          body: e,
        },
        options: {
          variant: "danger",
        },
      });
    }
    console.error(e);
  }
}

void loadBetting(props.bettingID);
watch(
  () => props.bettingID,
  (newBettingID) => {
    void loadBetting(newBettingID);
  }
);

function toggleCandidate(idx: number) {
  if (info.value === undefined) {
    return;
  }
  if (bettingDetailInfo.value === undefined) {
    return;
  }
  if (info.value.closeYearMonth < yearMonth.value) {
    return;
  }
  if (info.value.finished) {
    return;
  }
  const selectCnt = bettingDetailInfo.value.bettingInfo.selectCnt;

  if (selectCnt == 1) {
    pickedBetType.value.clear();
    pickedBetType.value.add(idx);
    pickedBetTypeKey.value = JSON.stringify([idx]);
    return;
  }

  if (pickedBetType.value.has(idx)) {
    pickedBetType.value.delete(idx);
  } else if (pickedBetType.value.size < selectCnt) {
    pickedBetType.value.add(idx);
  } else {
    emit("reqToast", {
      content: {
        title: "오류",
        body: `이미 ${selectCnt}개를 선택했습니다.`,
      },
      options: {
        variant: "warning",
      },
    });
    return;
  }

  const typeArr = Array.from(pickedBetType.value.values());
  pickedBetTypeKey.value = JSON.stringify(typeArr.sort((lhs, rhs) => lhs - rhs));
}

async function submitBet(): Promise<void> {
  const bettingInfo = info.value;
  if (bettingInfo === undefined) {
    return;
  }

  const bettingID = bettingInfo.id;
  const bettingType = JSON.parse(pickedBetTypeKey.value) as number[];
  const amount = betPoint.value;
  try {
    await SammoAPI.Betting.Bet({
      bettingID,
      bettingType,
      amount,
    });
    emit("reqToast", {
      content: {
        title: "완료",
        body: "베팅했습니다",
      },
      options: {
        variant: "success",
      },
    });
    await loadBetting(bettingInfo.id);
  } catch (e) {
    if (isString(e)) {
      emit("reqToast", {
        content: {
          title: "에러",
          body: e,
        },
        options: {
          variant: "danger",
        },
      });
    }
    console.error(e);
  }
}
</script>
