<script setup lang="ts">
import { computed, markRaw, reactive, ref, watch } from 'vue';
import { Plus } from '@element-plus/icons-vue';
import {
  fetchChangeProjectTaskStatus,
  fetchCreateProjectTask,
  fetchCreateProjectTaskAssignee,
  fetchDeleteProjectTask,
  fetchGetProjectExecutionAssignees,
  fetchGetProjectTask,
  fetchGetProjectTaskAssignees,
  fetchGetProjectTaskPage,
  fetchGetProjectTaskStatusBoard,
  fetchInactiveProjectTaskAssignee,
  fetchUpdateProjectTask
} from '@/service/api/project';
import { useAuthStore } from '@/store/modules/auth';
import { useUIPaginatedTable } from '@/hooks/common/table';
import { shouldRequireTaskProgressBeforeComplete } from '../shared';
import type { WorklogChangedPayload } from '../shared';
import { useTaskCompletionCascade } from '../composables/use-task-completion-cascade';
import { useTaskPermissions } from '../composables/use-task-permissions';
import ObjectDeleteDialog from './object-delete-dialog.vue';
import StatusActionDialog from './status-action-dialog.vue';
import TaskAssigneeDialog from './task-assignee-dialog.vue';
import TaskBoardView from './task-board-view.vue';
import TaskDetailDialog from './task-detail-dialog.vue';
import TaskOperateDialog from './task-operate-dialog.vue';
import TaskSearch from './task-search.vue';
import TaskTableView from './task-table-view.vue';
import TaskWorklogDialog from './task-worklog-dialog.vue';
import IconMdiViewColumnOutline from '~icons/mdi/view-column-outline';
import IconMdiTableLarge from '~icons/mdi/table-large';

defineOptions({ name: 'ProjectExecutionTaskWorkspace' });

type ViewMode = 'table' | 'board';
type OperateMode = 'create' | 'edit';
type TaskPageResponse = Awaited<ReturnType<typeof fetchGetProjectTaskPage>>;
type TaskStatusAction = Api.Project.LifecycleAction<Api.Project.ProjectTaskActionCode>;

interface Props {
  projectId: string;
  execution: Api.Project.ProjectExecution | null;
  canCreate: boolean;
}

interface Emits {
  (e: 'executionChanged'): void;
}

const props = defineProps<Props>();
const emit = defineEmits<Emits>();

const { canManageTaskAssignee } = useTaskPermissions();

const authStore = useAuthStore();
const currentUserId = computed(() => authStore.userInfo.userId || '');

// 当前执行的活跃协办人（owner / 协办人 / 搜索过滤都用这份）
const executionAssigneeOptions = ref<Api.SystemManage.UserSimple[]>([]);

const viewMode = ref<ViewMode>('table');
const viewModeOptions = [
  { label: '表格', value: 'table', icon: markRaw(IconMdiTableLarge) },
  { label: '看板', value: 'board', icon: markRaw(IconMdiViewColumnOutline) }
];
const operateVisible = ref(false);
const taskOperateDialogRef = ref<InstanceType<typeof TaskOperateDialog> | null>(null);
const statusActionVisible = ref(false);
const operateMode = ref<OperateMode>('create');
const currentTask = ref<Api.Project.ProjectTask | null>(null);
const presetParentTaskId = ref<string | null>(null);
const currentStatusAction = ref<TaskStatusAction | null>(null);
const taskStatusBoard = ref<Api.Project.StatusBoard | null>(null);

const pendingCascade = ref(false);

const assigneeDialogVisible = ref(false);
const currentAssigneeTask = ref<Api.Project.ProjectTask | null>(null);
const currentAssignees = ref<Api.Project.TaskAssigneeRef[]>([]);
const assigneesLoading = ref(false);

const worklogDialogVisible = ref(false);
const worklogDialogTask = ref<Api.Project.ProjectTask | null>(null);

const detailDialogVisible = ref(false);
const detailDialogTask = ref<Api.Project.ProjectTask | null>(null);
const detailDialogDefaultTab = ref<'info' | 'worklog'>('info');

const deleteTaskDialogVisible = ref(false);
const deleteTaskTarget = ref<Api.Project.ProjectTask | null>(null);

const searchParams = reactive<Api.Project.ProjectTaskSearchParams>({
  pageNo: 1,
  pageSize: 10,
  keyword: '',
  parentTaskId: undefined,
  ownerId: undefined,
  statusCode: undefined,
  updateTime: undefined
});

const executionId = computed(() => props.execution?.id || '');

const cascade = useTaskCompletionCascade({
  projectId: computed(() => props.projectId),
  executionId,
  openStatusActionDialog: (task, action, fromCascade) => {
    currentTask.value = task;
    currentStatusAction.value = action;
    pendingCascade.value = fromCascade;
    statusActionVisible.value = true;
  },
  resolveCompleteAction: task => task.availableActions.find(item => item.actionCode === 'complete') ?? null
});

const canLoadTasks = computed(() => Boolean(props.projectId && executionId.value));

const statusActionTitle = computed(() =>
  currentStatusAction.value ? `任务状态变更：${currentStatusAction.value.actionName}` : '任务状态变更'
);

function createRequestParams(): Api.Project.ProjectTaskSearchParams {
  return {
    ...searchParams,
    keyword: searchParams.keyword?.trim() || undefined
  };
}

function createStatusBoardParams(): Api.Project.ProjectTaskStatusBoardParams {
  return {
    keyword: searchParams.keyword?.trim() || undefined,
    parentTaskId: searchParams.parentTaskId,
    ownerId: searchParams.ownerId,
    updateTime: searchParams.updateTime
  };
}

function transformTaskPage(response: TaskPageResponse, pageNo: number, pageSize: number) {
  if (!response.error && response.data) {
    return {
      data: response.data.list,
      pageNum: pageNo,
      pageSize,
      total: response.data.total
    };
  }

  return {
    data: [],
    pageNum: pageNo,
    pageSize,
    total: 0
  };
}

const { data, loading, getData, getDataByPage, mobilePagination } = useUIPaginatedTable<
  TaskPageResponse,
  Api.Project.ProjectTask
>({
  paginationProps: {
    currentPage: searchParams.pageNo,
    pageSize: searchParams.pageSize
  },
  api: () => {
    if (!canLoadTasks.value) {
      return Promise.resolve({
        data: { total: 0, list: [] },
        error: null
      } as unknown as TaskPageResponse);
    }

    return fetchGetProjectTaskPage(props.projectId, executionId.value, createRequestParams());
  },
  transform: response => transformTaskPage(response, searchParams.pageNo ?? 1, searchParams.pageSize ?? 10),
  onPaginationParamsChange: params => {
    searchParams.pageNo = params.currentPage ?? 1;
    searchParams.pageSize = params.pageSize ?? 10;
  },
  columns: () => [{ prop: 'taskTitle', label: '任务名称', minWidth: 160 }]
});

const taskOptions = computed(() => data.value);

function resetSearchParams() {
  searchParams.pageNo = 1;
  searchParams.keyword = '';
  searchParams.parentTaskId = undefined;
  searchParams.ownerId = undefined;
  searchParams.statusCode = undefined;
  searchParams.updateTime = undefined;
}

async function handleSearch() {
  await Promise.all([getDataByPage(1), loadTaskStatusBoard()]);
}

async function handleReset() {
  resetSearchParams();
  await Promise.all([getDataByPage(1), loadTaskStatusBoard()]);
}

function handleCreate() {
  if (!props.execution) {
    window.$message?.warning('请先选择执行项');
    return;
  }

  operateMode.value = 'create';
  currentTask.value = null;
  presetParentTaskId.value = null;
  operateVisible.value = true;
}

async function getTaskDetail(row: Api.Project.ProjectTask) {
  if (!props.execution) {
    return row;
  }

  const result = await fetchGetProjectTask(props.projectId, props.execution.id, row.id);

  return result.error || !result.data ? row : result.data;
}

async function handleEdit(row: Api.Project.ProjectTask) {
  const detail = await getTaskDetail(row);

  if (!detail.allowEdit) {
    window.$message?.warning('当前任务状态不允许编辑');
    return;
  }

  operateMode.value = 'edit';
  currentTask.value = detail;
  operateVisible.value = true;
}

async function handleDetail(row: Api.Project.ProjectTask) {
  const detail = await getTaskDetail(row);
  detailDialogDefaultTab.value = 'info';
  detailDialogTask.value = detail;
  detailDialogVisible.value = true;
  // 同步到 currentTask，让 worklog tab 内提交后 handleWorklogChanged 能据此触发 cascade
  currentTask.value = detail;
}

async function handleStatusAction(row: Api.Project.ProjectTask, action: TaskStatusAction | null) {
  const detail = await getTaskDetail(row);
  const targetAction = action || detail.availableActions[0] || null;

  if (!targetAction) {
    window.$message?.warning('当前任务暂无可用状态操作');
    return;
  }

  if (shouldRequireTaskProgressBeforeComplete(targetAction, detail)) {
    window.$message?.warning('完成任务前请先将进度调整为 100%');
    return;
  }

  currentTask.value = detail;
  currentStatusAction.value = targetAction;
  statusActionVisible.value = true;
}

async function handleOperateSubmit(payload: Api.Project.SaveProjectTaskParams) {
  if (!props.execution) {
    return;
  }

  if (operateMode.value !== 'create' && !currentTask.value) {
    return;
  }

  const result =
    operateMode.value === 'create'
      ? await fetchCreateProjectTask(props.projectId, props.execution.id, payload)
      : await fetchUpdateProjectTask(props.projectId, props.execution.id, {
          taskId: currentTask.value!.id,
          data: payload
        });

  if (result.error) {
    return;
  }

  if (operateMode.value === 'create') {
    window.$message?.success('任务创建成功');
  } else {
    window.$message?.success('任务更新成功');
  }

  // 业务保存成功才 commit：删除用户在弹层里标记删除的附件
  await taskOperateDialogRef.value?.commit();
  operateVisible.value = false;
  await getData();
}

async function handleStatusSubmit(reason: string | null) {
  if (!props.execution || !currentTask.value || !currentStatusAction.value) {
    return;
  }

  const result = await fetchChangeProjectTaskStatus(props.projectId, props.execution.id, {
    taskId: currentTask.value.id,
    data: {
      actionCode: currentStatusAction.value.actionCode,
      reason
    }
  });

  if (result.error) {
    return;
  }

  window.$message?.success('任务状态已更新');
  statusActionVisible.value = false;

  const wasCascade = pendingCascade.value;
  const completedTask = currentTask.value;
  pendingCascade.value = false;

  await Promise.all([getData(), loadTaskStatusBoard()]);
  emit('executionChanged');

  if (wasCascade && completedTask) {
    await cascade.onTaskCompleted(completedTask);
  }
}

async function handleReport(row: Api.Project.ProjectTask) {
  if (!props.execution) {
    window.$message?.warning('请先选择执行项');
    return;
  }

  const detail = await getTaskDetail(row);
  currentTask.value = detail;

  // 责任人 / 协办人统一走轻量弹层；内部按身份切换"全员记录 vs 仅自己"
  // 责任人想看任务全貌请改走【任务名】打开的详情弹框
  worklogDialogTask.value = detail;
  worklogDialogVisible.value = true;
}

async function handleWorklogChanged(payload: WorklogChangedPayload) {
  await Promise.all([getData(), loadTaskStatusBoard()]);

  // 工时变化可能触发后端联动改任务状态、实际开始、进度等；
  // 同步刷新当前打开弹层的 task 快照，避免用户必须关弹层重开才能看到新值
  if (props.execution && currentTask.value?.id === payload.taskId) {
    const refreshed = await getTaskDetail(currentTask.value);
    if (refreshed.id === payload.taskId) {
      currentTask.value = refreshed;
      if (worklogDialogTask.value?.id === payload.taskId) {
        worklogDialogTask.value = refreshed;
      }
      if (detailDialogTask.value?.id === payload.taskId) {
        detailDialogTask.value = refreshed;
      }
    }
  }

  if (payload.mode === 'delete' || payload.progressRate !== 100) {
    return;
  }

  const task = currentTask.value;
  // 防御：currentTask 不对应当前 worklog 任务（理论不会，但兜底）
  if (!task || task.id !== payload.taskId || task.ownerId !== currentUserId.value) {
    return;
  }

  await cascade.triggerAfterWorklog({ task, submittedProgress: payload.progressRate });
}

async function handleDetailWorklogChanged(payload: WorklogChangedPayload) {
  await handleWorklogChanged(payload);
  emit('executionChanged');
}

function handleWorklogDialogClosedAfterChange() {
  emit('executionChanged');
}

async function handleAssigneeAdd(payload: Api.Project.CreateTaskAssigneeParams) {
  if (!props.execution || !currentAssigneeTask.value) {
    return;
  }

  const { error } = await fetchCreateProjectTaskAssignee(
    props.projectId,
    props.execution.id,
    currentAssigneeTask.value.id,
    payload
  );

  if (error) {
    return;
  }

  window.$message?.success('协办人已加入');
  await refreshAssigneesAfterMutation();
}

async function handleAssigneeInactive(
  assignee: Api.Project.TaskAssigneeRef,
  payload: Api.Project.InactiveTaskAssigneeParams
) {
  if (!props.execution || !currentAssigneeTask.value) {
    return;
  }

  const { error } = await fetchInactiveProjectTaskAssignee(
    props.projectId,
    props.execution.id,
    currentAssigneeTask.value.id,
    assignee.id,
    payload
  );

  if (error) {
    return;
  }

  window.$message?.success('协办人已失效');
  await refreshAssigneesAfterMutation();
}

async function refreshAssigneesAfterMutation() {
  if (!props.execution || !currentAssigneeTask.value) {
    return;
  }

  assigneesLoading.value = true;
  const { error, data: assigneeData } = await fetchGetProjectTaskAssignees(
    props.projectId,
    props.execution.id,
    currentAssigneeTask.value.id
  );
  assigneesLoading.value = false;
  currentAssignees.value = error || !assigneeData ? [] : assigneeData;

  await getData();
}

async function loadExecutionAssigneeOptions() {
  if (!canLoadTasks.value) {
    executionAssigneeOptions.value = [];
    return;
  }

  const { error, data: assignees } = await fetchGetProjectExecutionAssignees(props.projectId, executionId.value);

  if (error || !assignees) {
    executionAssigneeOptions.value = [];
    return;
  }

  executionAssigneeOptions.value = assignees.map(item => ({
    id: item.userId,
    nickname: item.userNickname || item.userId,
    username: null,
    deptName: null
  }));
}

function openDeleteTaskDialog(task: Api.Project.ProjectTask) {
  deleteTaskTarget.value = task;
  deleteTaskDialogVisible.value = true;
}

async function confirmDeleteTask(payload: { name: string; confirmText: string; reason: string }) {
  const target = deleteTaskTarget.value;
  if (!target) return;
  const { error } = await fetchDeleteProjectTask(target.projectId, target.executionId, target.id, {
    taskName: payload.name,
    confirmText: payload.confirmText,
    reason: payload.reason
  });
  if (error) return;
  window.$message?.success('删除成功');
  deleteTaskDialogVisible.value = false;
  deleteTaskTarget.value = null;
  await Promise.all([getData(), loadTaskStatusBoard()]);
}

async function loadTaskStatusBoard() {
  if (!canLoadTasks.value) {
    taskStatusBoard.value = null;
    return;
  }

  const { error, data: board } = await fetchGetProjectTaskStatusBoard(
    props.projectId,
    executionId.value,
    createStatusBoardParams()
  );

  taskStatusBoard.value = error || !board ? null : board;
}

watch(
  () => props.execution?.id,
  async value => {
    resetSearchParams();

    if (!value) {
      data.value = [];
      taskStatusBoard.value = null;
      executionAssigneeOptions.value = [];
      return;
    }

    await Promise.all([getDataByPage(1), loadTaskStatusBoard(), loadExecutionAssigneeOptions()]);
  },
  { immediate: true }
);
</script>

<template>
  <section class="task-workspace">
    <header class="task-workspace__header">
      <h3 class="task-workspace__title">任务</h3>

      <div class="task-workspace__actions">
        <ElSegmented v-model="viewMode" class="task-view-toggle" size="default" :options="viewModeOptions">
          <template #default="{ item }">
            <span class="task-view-toggle__item">
              <component :is="item.icon" class="task-view-toggle__icon" />
              <span>{{ item.label }}</span>
            </span>
          </template>
        </ElSegmented>
        <ElButton v-if="canCreate" type="primary" :icon="Plus" :disabled="!execution" @click="handleCreate">
          新增
        </ElButton>
      </div>
    </header>

    <TaskSearch
      :model="searchParams"
      :user-options="executionAssigneeOptions"
      :status-options="taskStatusBoard?.items || []"
      :disabled="!execution"
      @search="handleSearch"
      @reset="handleReset"
    />

    <ElEmpty v-if="!execution" class="task-workspace__empty" description="请选择左侧执行项" />
    <TaskTableView
      v-else-if="viewMode === 'table'"
      :data="data"
      :loading="loading"
      :pagination="mobilePagination"
      @detail="handleDetail"
      @edit="handleEdit"
      @report="handleReport"
      @status-action="handleStatusAction"
      @delete="openDeleteTaskDialog"
    />
    <TaskBoardView
      v-else
      :data="data"
      :loading="loading"
      :status-board="taskStatusBoard"
      @detail="handleDetail"
      @edit="handleEdit"
      @status-action="handleStatusAction"
    />

    <div v-if="execution && viewMode === 'board' && mobilePagination.total" class="task-workspace__board-pagination">
      <ElPagination
        background
        layout="total, sizes, prev, pager, next"
        v-bind="mobilePagination"
        @current-change="mobilePagination['current-change']"
        @size-change="mobilePagination['size-change']"
      />
    </div>

    <TaskOperateDialog
      ref="taskOperateDialogRef"
      v-model:visible="operateVisible"
      :mode="operateMode"
      :row-data="currentTask"
      :default-parent-task-id="presetParentTaskId"
      :user-options="executionAssigneeOptions"
      :task-options="taskOptions"
      @submit="handleOperateSubmit"
    />

    <StatusActionDialog
      v-model:visible="statusActionVisible"
      :title="statusActionTitle"
      :action="currentStatusAction"
      @submit="handleStatusSubmit"
      @update:visible="
        value => {
          if (!value) pendingCascade = false;
        }
      "
    />

    <TaskAssigneeDialog
      v-model:visible="assigneeDialogVisible"
      :task="currentAssigneeTask"
      :assignees="currentAssignees"
      :user-options="executionAssigneeOptions"
      :loading="assigneesLoading"
      :can-manage="currentAssigneeTask ? canManageTaskAssignee(currentAssigneeTask) : false"
      @add="handleAssigneeAdd"
      @inactive="handleAssigneeInactive"
    />

    <TaskWorklogDialog
      v-model:visible="worklogDialogVisible"
      :task="worklogDialogTask"
      @changed="handleWorklogChanged"
      @closed-after-change="handleWorklogDialogClosedAfterChange"
    />

    <TaskDetailDialog
      v-model:visible="detailDialogVisible"
      :task="detailDialogTask"
      :user-options="executionAssigneeOptions"
      :task-options="taskOptions"
      :default-tab="detailDialogDefaultTab"
      @worklog-changed="handleDetailWorklogChanged"
    />

    <ObjectDeleteDialog
      v-model:visible="deleteTaskDialogVisible"
      object-type="task"
      :object-name="deleteTaskTarget?.taskTitle ?? ''"
      :on-confirm="confirmDeleteTask"
    />
  </section>
</template>

<style scoped lang="scss">
.task-workspace {
  display: flex;
  min-width: 0;
  min-height: 0;
  flex: 1;
  flex-direction: column;
  gap: 12px;
  height: 100%;
  padding: 12px;
  border: 1px solid rgb(226 232 240 / 92%);
  border-radius: 8px;
  background-color: #fff;
}

.task-workspace__header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  min-height: 40px;
}

.task-workspace__title {
  margin: 0;
  color: rgb(15 23 42 / 94%);
  font-size: 16px;
  font-weight: 700;
  line-height: 1.4;
}

.task-workspace__actions {
  display: flex;
  align-items: center;
  gap: 12px;
  flex: 0 0 auto;
}

.task-view-toggle {
  --el-segmented-item-selected-bg-color: var(--el-color-primary);
  --el-segmented-item-selected-color: #fff;
}

.task-view-toggle__item {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 0 4px;
  font-weight: 500;
}

.task-view-toggle__icon {
  font-size: 16px;
}

.task-workspace__board-pagination {
  display: flex;
  justify-content: flex-end;
}

.task-workspace__empty {
  flex: 1;
  border: 1px dashed rgb(203 213 225 / 92%);
  border-radius: 8px;
  background-color: #fff;
}
</style>
