<template>
  <div class="p-2">
    <transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
      <div v-show="showSearch" class="mb-[10px]">
        <el-card shadow="hover">
          <el-form ref="queryFormRef" :model="queryParams" :inline="true">
            <el-form-item label="类型" prop="type">
              <el-select v-model="queryParams.type" placeholder="请选择类型" clearable>
                <el-option v-for="item in priceTypeOptions" :key="item.value" :label="item.label" :value="item.value" />
              </el-select>
            </el-form-item>
            <el-form-item label="基准价格" prop="isBasePrice">
              <el-select v-model="queryParams.isBasePrice" placeholder="请选择" clearable>
                <el-option label="是" value="1" />
                <el-option label="否" value="0" />
              </el-select>
            </el-form-item>
            <el-form-item>
              <el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
              <el-button icon="Refresh" @click="resetQuery">重置</el-button>
            </el-form-item>
          </el-form>
        </el-card>
      </div>
    </transition>

    <el-card shadow="hover">
      <template #header>
        <el-row :gutter="10">
          <el-col :span="1.5">
            <el-button type="primary" plain icon="Plus" @click="handleAdd">新增</el-button>
          </el-col>
          <el-col :span="1.5">
            <el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()">修改</el-button>
          </el-col>
          <el-col :span="1.5">
            <el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()">删除</el-button>
          </el-col>
          <el-col :span="1.5">
            <el-button type="warning" plain icon="DataLine" @click="handleOpenCoverage">覆盖检查</el-button>
          </el-col>
          <right-toolbar v-model:show-search="showSearch" @query-table="getList" />
        </el-row>
      </template>

      <el-table v-loading="loading" border :data="priceList" @selection-change="handleSelectionChange">
        <el-table-column type="selection" width="55" align="center" />
        <el-table-column label="ID" prop="id" min-width="160" />
        <el-table-column label="类型" prop="type" align="center" width="90" />
        <el-table-column label="开始日期" prop="startDate" align="center" width="100" />
        <el-table-column label="结束日期" prop="endDate" align="center" width="100" />
        <el-table-column label="开始时间" prop="startTime" align="center" width="120" />
        <el-table-column label="结束时间" prop="endTime" align="center" width="120" />
        <el-table-column label="价格" prop="price" align="right" width="120">
          <template #default="scope">
            {{ formatPrice(scope.row.price) }}
          </template>
        </el-table-column>
        <el-table-column label="基准价格" prop="isBasePrice" align="center" width="100">
          <template #default="scope">
            <el-tag :type="scope.row.isBasePrice === '1' ? 'success' : 'info'">{{ scope.row.isBasePrice === '1' ? '是' : '否' }}</el-tag>
          </template>
        </el-table-column>
        <el-table-column label="预留1" prop="reserve1" min-width="120" />
        <el-table-column label="预留2" prop="reserve2" min-width="120" />
        <el-table-column label="预留3" prop="reserve3" min-width="120" />
        <el-table-column label="更新时间" prop="updateTime" align="center" width="170" />
        <el-table-column fixed="right" label="操作" align="center" width="120">
          <template #default="scope">
            <el-tooltip content="修改" placement="top">
              <el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" />
            </el-tooltip>
            <el-tooltip content="删除" placement="top">
              <el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" />
            </el-tooltip>
          </template>
        </el-table-column>
      </el-table>

      <pagination v-show="total > 0" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" :total="total" @pagination="getList" />
    </el-card>

    <el-dialog v-model="dialog.visible" :title="dialog.title" append-to-body width="620px">
      <el-form ref="priceFormRef" :model="form" :rules="rules" label-width="100px">
        <el-form-item label="基准价格" prop="isBasePrice">
          <el-switch v-model="isBasePriceSwitch" active-text="是" inactive-text="否" @change="handleBasePriceChange" />
        </el-form-item>
        <el-form-item v-if="form.isBasePrice !== '1'" label="类型" prop="type">
          <el-select v-model="form.type" placeholder="请选择类型">
            <el-option v-for="item in priceTypeOptions" :key="item.value" :label="item.label" :value="item.value" />
          </el-select>
        </el-form-item>
        <template v-if="form.isBasePrice !== '1'">
          <el-form-item label="开始日期" prop="startDate">
            <el-space>
              <el-select v-model="dateParts.startMonth" placeholder="月份" style="width: 110px" @change="handleDatePartChange('start')">
                <el-option v-for="item in monthOptions" :key="item.value" :label="item.label" :value="item.value" />
              </el-select>
              <el-select v-model="dateParts.startDay" placeholder="日期" style="width: 110px" @change="handleDatePartChange('start')">
                <el-option v-for="item in getDayOptions(dateParts.startMonth)" :key="item.value" :label="item.label" :value="item.value" />
              </el-select>
            </el-space>
          </el-form-item>
          <el-form-item label="结束日期" prop="endDate">
            <el-space>
              <el-select v-model="dateParts.endMonth" placeholder="月份" style="width: 110px" @change="handleDatePartChange('end')">
                <el-option v-for="item in monthOptions" :key="item.value" :label="item.label" :value="item.value" />
              </el-select>
              <el-select v-model="dateParts.endDay" placeholder="日期" style="width: 110px" @change="handleDatePartChange('end')">
                <el-option v-for="item in getDayOptions(dateParts.endMonth)" :key="item.value" :label="item.label" :value="item.value" />
              </el-select>
            </el-space>
          </el-form-item>
          <el-form-item label="开始时间" prop="startTime">
            <el-time-picker v-model="form.startTime" value-format="HH:mm:ss" placeholder="请选择开始时间" />
          </el-form-item>
          <el-form-item label="结束时间" prop="endTime">
            <el-time-picker v-model="form.endTime" value-format="HH:mm:ss" placeholder="请选择结束时间" />
          </el-form-item>
        </template>
        <el-form-item label="价格" prop="price">
          <el-input-number v-model="form.price" :min="0" :precision="6" :step="0.01" controls-position="right" />
        </el-form-item>
        <el-form-item label="预留1" prop="reserve1">
          <el-input v-model="form.reserve1" placeholder="请输入预留字段1" />
        </el-form-item>
        <el-form-item label="预留2" prop="reserve2">
          <el-input v-model="form.reserve2" placeholder="请输入预留字段2" />
        </el-form-item>
        <el-form-item label="预留3" prop="reserve3">
          <el-input v-model="form.reserve3" placeholder="请输入预留字段3" />
        </el-form-item>
      </el-form>
      <template #footer>
        <div class="dialog-footer">
          <el-button type="primary" @click="submitForm">确 定</el-button>
          <el-button @click="cancel">取 消</el-button>
        </div>
      </template>
    </el-dialog>

    <el-dialog v-model="coverageDialog.visible" title="电价覆盖检查" append-to-body width="900px">
      <div v-loading="coverageLoading" class="coverage-dialog">
        <div class="coverage-summary">
          <el-space>
            <el-tag type="success">基准价格不参与判断</el-tag>
            <el-tag :type="coverageSummary.missingDays ? 'warning' : 'success'">
              未完整设置日期：{{ coverageSummary.missingDays }} 天
            </el-tag>
          </el-space>
        </div>
        <div class="coverage-timeline">
          <div
            v-for="item in coverageTimeline"
            :key="item.key"
            class="coverage-timeline__segment"
            :class="{ 'is-missing': item.missing }"
            :title="item.label"
          />
        </div>
        <div class="coverage-legend">
          <span><i class="coverage-dot coverage-dot--covered" />已设置电价</span>
          <span><i class="coverage-dot coverage-dot--missing" />未设置电价</span>
        </div>
        <el-tree
          class="coverage-tree"
          :data="coverageTree"
          node-key="id"
          :props="{ label: 'label', children: 'children' }"
        >
          <template #default="{ data: node }">
            <span :class="{ 'coverage-node--missing': node.missing }">{{ node.label }}</span>
          </template>
        </el-tree>
      </div>
    </el-dialog>
  </div>
</template>

<script setup name="ElectricityPrices" lang="ts">
import {
  addElectricityPrice,
  delElectricityPrice,
  getElectricityPrice,
  listElectricityPrices,
  updateElectricityPrice
} from '@/api/electricityManagement/electricityPrices';
import {
  ElectricityPriceForm,
  ElectricityPriceQuery,
  ElectricityPriceVO
} from '@/api/electricityManagement/electricityPrices/types';

interface CoverageNode {
  id: string;
  label: string;
  missing: boolean;
  children?: CoverageNode[];
}

interface CoverageTimelineItem {
  key: string;
  label: string;
  missing: boolean;
}

const { proxy } = getCurrentInstance() as ComponentInternalInstance;

const priceList = ref<ElectricityPriceVO[]>([]);
const loading = ref(false);
const coverageLoading = ref(false);
const showSearch = ref(true);
const total = ref(0);
const ids = ref<Array<string | number>>([]);
const single = ref(true);
const multiple = ref(true);
const isBasePriceSwitch = ref(false);
const coverageTree = ref<CoverageNode[]>([]);
const coverageTimeline = ref<CoverageTimelineItem[]>([]);
const coverageSummary = reactive({
  missingDays: 0
});
const dateParts = reactive({
  startMonth: '',
  startDay: '',
  endMonth: '',
  endDay: ''
});
const priceTypeOptions = [
  { label: '峰', value: '峰' },
  { label: '平', value: '平' },
  { label: '谷', value: '谷' }
];
const monthOptions = Array.from({ length: 12 }, (_item, index) => {
  const value = String(index + 1).padStart(2, '0');
  return {
    label: `${index + 1}月`,
    value
  };
});
const daysByMonth: Record<string, number> = {
  '01': 31,
  '02': 29,
  '03': 31,
  '04': 30,
  '05': 31,
  '06': 30,
  '07': 31,
  '08': 31,
  '09': 30,
  '10': 31,
  '11': 30,
  '12': 31
};

const queryFormRef = ref<ElFormInstance>();
const priceFormRef = ref<ElFormInstance>();

const validateMonthDay = (_rule: unknown, value: string, callback: (error?: Error) => void) => {
  if (!value) {
    callback(new Error('日期不能为空'));
    return;
  }
  if (!/^\d{2}-\d{2}$/.test(value)) {
    callback(new Error('日期格式必须为MM-DD'));
    return;
  }
  const date = new Date(`2000-${value}T00:00:00`);
  const normalized = Number.isNaN(date.getTime())
    ? ''
    : `${String(date.getMonth() + 1).padStart(2, '0')}-${String(date.getDate()).padStart(2, '0')}`;
  if (normalized !== value) {
    callback(new Error('日期格式必须为MM-DD'));
    return;
  }
  callback();
};

const dialog = reactive<DialogOption>({
  visible: false,
  title: ''
});
const coverageDialog = reactive<DialogOption>({
  visible: false,
  title: ''
});

const initFormData: ElectricityPriceForm = {
  id: undefined,
  type: undefined,
  startDate: '',
  endDate: '',
  startTime: '',
  endTime: '',
  price: undefined,
  isBasePrice: '0',
  reserve1: '',
  reserve2: '',
  reserve3: ''
};

const data = reactive<PageData<ElectricityPriceForm, ElectricityPriceQuery>>({
  form: { ...initFormData },
  queryParams: {
    pageNum: 1,
    pageSize: 10,
    type: undefined,
    isBasePrice: undefined
  },
  rules: {
    type: [{ required: true, message: '类型不能为空', trigger: 'change' }],
    startDate: [{ required: true, validator: validateMonthDay, trigger: 'change' }],
    endDate: [{ required: true, validator: validateMonthDay, trigger: 'change' }],
    startTime: [{ required: true, message: '开始时间不能为空', trigger: 'change' }],
    endTime: [{ required: true, message: '结束时间不能为空', trigger: 'change' }],
    price: [{ required: true, message: '价格不能为空', trigger: 'blur' }]
  }
});

const { queryParams, form, rules } = toRefs(data);

const formatPrice = (value?: number) => {
  const numberValue = Number(value);
  return Number.isFinite(numberValue) ? numberValue.toFixed(6) : '-';
};

const toSecond = (time?: string) => {
  const [hour = 0, minute = 0, second = 0] = String(time || '00:00:00').split(':').map(Number);
  return hour * 3600 + minute * 60 + second;
};

const formatTime = (second: number) => {
  const bounded = Math.max(0, Math.min(second, 86399));
  const hour = Math.floor(bounded / 3600);
  const minute = Math.floor((bounded % 3600) / 60);
  const currentSecond = bounded % 60;
  return [hour, minute, currentSecond].map((item) => String(item).padStart(2, '0')).join(':');
};

const monthDay = (month: string, day: string) => `${month}-${day}`;

const eachMonthDay = () => monthOptions.flatMap((month) => (
  getDayOptions(month.value).map((day) => ({
    key: monthDay(month.value, day.value),
    month: month.value,
    monthLabel: month.label,
    day: day.value,
    dayLabel: day.label
  }))
));

const getDateRangeKeys = (startDate?: string, endDate?: string) => {
  const days = eachMonthDay();
  const startIndex = days.findIndex((item) => item.key === startDate);
  const endIndex = days.findIndex((item) => item.key === endDate);
  if (startIndex < 0 || endIndex < 0) {
    return [];
  }
  if (startIndex <= endIndex) {
    return days.slice(startIndex, endIndex + 1).map((item) => item.key);
  }
  return [...days.slice(startIndex), ...days.slice(0, endIndex + 1)].map((item) => item.key);
};

const getTimeSegments = (startTime?: string, endTime?: string) => {
  const start = toSecond(startTime);
  const rawEnd = toSecond(endTime);
  const end = rawEnd === 86399 ? 86400 : rawEnd;
  if (start < end) {
    return [[start, end]];
  }
  if (start > end) {
    return [[start, 86400], [0, end]];
  }
  return [];
};

const formatPriceText = (value?: number) => {
  const numberValue = Number(value);
  return Number.isFinite(numberValue) ? `${numberValue.toFixed(6)}元` : '';
};

const mergePriceSegments = (segments: Array<{ start: number; end: number; price: number }>) => {
  const sorted = segments
    .filter((item) => item.start < item.end)
    .sort((left, right) => left.start - right.start || left.end - right.end);
  return sorted.reduce<Array<{ start: number; end: number; price: number }>>((merged, current) => {
    const last = merged[merged.length - 1];
    if (!last || current.start > last.end || Number(current.price) !== Number(last.price)) {
      merged.push({ ...current });
      return merged;
    }
    last.end = Math.max(last.end, current.end);
    return merged;
  }, []);
};

const toCoverageSegments = (segments: Array<{ start: number; end: number; price: number }>) => (
  mergePriceSegments(segments).map((item) => [item.start, item.end])
);

const getMissingSegments = (segments: number[][]) => {
  const missing: number[][] = [];
  let cursor = 0;
  segments.forEach(([start, end]) => {
    if (cursor < start) {
      missing.push([cursor, start]);
    }
    cursor = Math.max(cursor, end);
  });
  if (cursor < 86400) {
    missing.push([cursor, 86400]);
  }
  return missing;
};

const isFullDayCovered = (segments: number[][]) => getMissingSegments(segments).length === 0;

const buildDayChildren = (dayId: string, priceSegments: Array<{ start: number; end: number; price: number }>) => {
  const mergedPriceSegments = mergePriceSegments(priceSegments);
  const children: CoverageNode[] = mergedPriceSegments.map((item, index) => ({
    id: `${dayId}-price-${index}`,
    label: `${formatTime(item.start)} - ${formatTime(item.end - 1)} ${formatPriceText(item.price)}`,
    missing: false
  }));
  getMissingSegments(toCoverageSegments(mergedPriceSegments)).forEach(([start, end], index) => {
    children.push({
      id: `${dayId}-missing-${index}`,
      label: `未设置 ${formatTime(start)} - ${formatTime(end - 1)}`,
      missing: true
    });
  });
  children.sort((left, right) => {
    const leftSecond = left.label.startsWith('未设置') ? toSecond(left.label.slice(4, 12)) : toSecond(left.label);
    const rightSecond = right.label.startsWith('未设置') ? toSecond(right.label.slice(4, 12)) : toSecond(right.label);
    return leftSecond - rightSecond;
  });
  return children;
};

const buildCoverageData = (rows: ElectricityPriceVO[]) => {
  const daySegments = new Map<string, Array<{ start: number; end: number; price: number }>>();
  eachMonthDay().forEach((item) => daySegments.set(item.key, []));

  rows
    .filter((item) => item.isBasePrice !== '1' && item.startDate && item.endDate && item.startTime && item.endTime)
    .forEach((item) => {
      const timeSegments = getTimeSegments(item.startTime, item.endTime);
      getDateRangeKeys(item.startDate, item.endDate).forEach((dayKey) => {
        daySegments.get(dayKey)?.push(...timeSegments.map(([start, end]) => ({
          start,
          end,
          price: Number(item.price || 0)
        })));
      });
    });

  const allDays = eachMonthDay();
  const timeline: CoverageTimelineItem[] = [];
  let missingDays = 0;
  const monthNodes = monthOptions.map<CoverageNode>((month) => {
    const dayNodes = getDayOptions(month.value).map<CoverageNode>((day) => {
      const key = monthDay(month.value, day.value);
      const priceSegments = mergePriceSegments(daySegments.get(key) || []);
      const full = isFullDayCovered(toCoverageSegments(priceSegments));
      if (!full) {
        missingDays += 1;
      }
      timeline.push({
        key,
        label: `${month.label}${day.label}`,
        missing: !full
      });
      return {
        id: `day-${key}`,
        label: `${day.label}`,
        missing: !full,
        children: buildDayChildren(`day-${key}`, priceSegments)
      };
    });
    return {
      id: `month-${month.value}`,
      label: month.label,
      missing: dayNodes.some((item) => item.missing),
      children: dayNodes
    };
  });

  coverageTree.value = monthNodes;
  coverageTimeline.value = timeline.length ? timeline : allDays.map((item) => ({
    key: item.key,
    label: `${item.monthLabel}${item.dayLabel}`,
    missing: true
  }));
  coverageSummary.missingDays = missingDays || (timeline.length ? 0 : allDays.length);
};

const getDayOptions = (month: string) => {
  const count = daysByMonth[month] || 31;
  return Array.from({ length: count }, (_item, index) => {
    const value = String(index + 1).padStart(2, '0');
    return {
      label: `${index + 1}日`,
      value
    };
  });
};

const syncDatePartsFromForm = () => {
  const [startMonth = '', startDay = ''] = String(form.value.startDate || '').split('-');
  const [endMonth = '', endDay = ''] = String(form.value.endDate || '').split('-');
  dateParts.startMonth = startMonth;
  dateParts.startDay = startDay;
  dateParts.endMonth = endMonth;
  dateParts.endDay = endDay;
};

const clearDateParts = () => {
  dateParts.startMonth = '';
  dateParts.startDay = '';
  dateParts.endMonth = '';
  dateParts.endDay = '';
};

const handleDatePartChange = (type: 'start' | 'end') => {
  const monthKey = type === 'start' ? 'startMonth' : 'endMonth';
  const dayKey = type === 'start' ? 'startDay' : 'endDay';
  const formKey = type === 'start' ? 'startDate' : 'endDate';
  const maxDay = daysByMonth[dateParts[monthKey]] || 31;
  if (Number(dateParts[dayKey]) > maxDay) {
    dateParts[dayKey] = '';
  }
  form.value[formKey] = dateParts[monthKey] && dateParts[dayKey] ? `${dateParts[monthKey]}-${dateParts[dayKey]}` : '';
  priceFormRef.value?.validateField(formKey);
};

const getList = async () => {
  loading.value = true;
  try {
    const res = await listElectricityPrices(queryParams.value);
    priceList.value = res.rows || [];
    total.value = res.total || 0;
  } finally {
    loading.value = false;
  }
};

const reset = () => {
  form.value = { ...initFormData };
  isBasePriceSwitch.value = false;
  clearDateParts();
  priceFormRef.value?.resetFields();
};

const handleQuery = () => {
  queryParams.value.pageNum = 1;
  getList();
};

const resetQuery = () => {
  queryFormRef.value?.resetFields();
  handleQuery();
};

const handleSelectionChange = (selection: ElectricityPriceVO[]) => {
  ids.value = selection.map((item) => item.id);
  single.value = selection.length !== 1;
  multiple.value = !selection.length;
};

const handleBasePriceChange = (value: string | number | boolean) => {
  form.value.isBasePrice = value ? '1' : '0';
  if (form.value.isBasePrice === '1') {
    form.value.type = undefined;
    form.value.startDate = '';
    form.value.endDate = '';
    form.value.startTime = '';
    form.value.endTime = '';
    clearDateParts();
  }
  priceFormRef.value?.clearValidate();
};

const handleAdd = () => {
  reset();
  dialog.visible = true;
  dialog.title = '新增电价';
};

const handleUpdate = async (row?: ElectricityPriceVO) => {
  reset();
  const id = row?.id || ids.value[0];
  const res = await getElectricityPrice(id);
  form.value = { ...res.data };
  isBasePriceSwitch.value = form.value.isBasePrice === '1';
  syncDatePartsFromForm();
  dialog.visible = true;
  dialog.title = '修改电价';
};

const submitForm = () => {
  priceFormRef.value?.validate(async (valid: boolean) => {
    if (!valid) {
      return;
    }
    if (form.value.id) {
      await updateElectricityPrice(form.value);
    } else {
      await addElectricityPrice(form.value);
    }
    proxy?.$modal.msgSuccess('操作成功');
    dialog.visible = false;
    await getList();
  });
};

const handleDelete = async (row?: ElectricityPriceVO) => {
  const deleteIds = row?.id || ids.value;
  await proxy?.$modal.confirm('是否确认删除电价编号为"' + deleteIds + '"的数据项？');
  await delElectricityPrice(deleteIds);
  proxy?.$modal.msgSuccess('删除成功');
  await getList();
};

const handleOpenCoverage = async () => {
  coverageDialog.visible = true;
  coverageLoading.value = true;
  try {
    const res = await listElectricityPrices({
      pageNum: 1,
      pageSize: 10000,
      isBasePrice: '0'
    });
    buildCoverageData(res.rows || []);
  } finally {
    coverageLoading.value = false;
  }
};

const cancel = () => {
  dialog.visible = false;
  reset();
};

onMounted(() => {
  getList();
});
</script>

<style scoped>
.coverage-dialog {
  min-height: 420px;
}

.coverage-summary {
  margin-bottom: 12px;
}

.coverage-timeline {
  display: flex;
  height: 14px;
  overflow: hidden;
  border: 1px solid #dcdfe6;
  border-radius: 2px;
  background: #dcdfe6;
}

.coverage-timeline__segment {
  flex: 1 1 0;
  min-width: 1px;
  background: transparent;
}

.coverage-timeline__segment.is-missing {
  background: #409eff;
}

.coverage-legend {
  display: flex;
  gap: 18px;
  margin: 10px 0 14px;
  color: #606266;
  font-size: 13px;
}

.coverage-dot {
  display: inline-block;
  width: 18px;
  height: 8px;
  margin-right: 6px;
  border: 1px solid #dcdfe6;
  vertical-align: middle;
}

.coverage-dot--covered {
  background: transparent;
}

.coverage-dot--missing {
  background: #409eff;
  border-color: #409eff;
}

.coverage-tree {
  max-height: 520px;
  overflow: auto;
  border: 1px solid #ebeef5;
  border-radius: 4px;
  padding: 8px 0;
}

.coverage-node--missing {
  color: #409eff;
}
</style>
