import type { ListScreenDefinition } from "@/lib/funnel/types";
import type { BuilderScreen } from "@/lib/admin/builder/types";
import type { BuilderState, BuilderAction } from "./types";
import { INITIAL_STATE } from "./constants";
import { withDirty, generateScreenId, createScreenByTemplate } from "./utils";

/**
 * Обновляет все ссылки на oldScreenId на newScreenId во всех экранах
 */
function updateScreenIdReferences(
  screens: BuilderScreen[],
  oldScreenId: string,
  newScreenId: string
): BuilderScreen[] {
  return screens.map((screen) => {
    let updated = false;
    const updatedScreen = { ...screen };

    // Обновляем navigation.defaultNextScreenId
    if (updatedScreen.navigation?.defaultNextScreenId === oldScreenId) {
      updatedScreen.navigation = {
        ...updatedScreen.navigation,
        defaultNextScreenId: newScreenId,
      };
      updated = true;
    }

    // Обновляем navigation.rules[].nextScreenId
    if (updatedScreen.navigation?.rules && updatedScreen.navigation.rules.length > 0) {
      const updatedRules = updatedScreen.navigation.rules.map((rule) => {
        if (rule.nextScreenId === oldScreenId) {
          return { ...rule, nextScreenId: newScreenId };
        }
        return rule;
      });
      
      if (updatedRules.some((rule, index) => rule !== updatedScreen.navigation!.rules![index])) {
        updatedScreen.navigation = {
          ...updatedScreen.navigation,
          rules: updatedRules,
        };
        updated = true;
      }
    }

    // Обновляем variants[].conditions[].screenId
    if (updatedScreen.variants && updatedScreen.variants.length > 0) {
      const updatedVariants = updatedScreen.variants.map((variant) => {
        if (!variant.conditions) return variant;
        
        const updatedConditions = variant.conditions.map((condition) => {
          if (condition.screenId === oldScreenId) {
            return { ...condition, screenId: newScreenId };
          }
          return condition;
        });
        
        if (updatedConditions.some((cond, index) => cond !== variant.conditions![index])) {
          return { ...variant, conditions: updatedConditions };
        }
        return variant;
      });
      
      if (updatedVariants.some((v, index) => v !== updatedScreen.variants![index])) {
        updatedScreen.variants = updatedVariants;
        updated = true;
      }
    }

    return updated ? updatedScreen : screen;
  });
}

export function builderReducer(state: BuilderState, action: BuilderAction): BuilderState {
  switch (action.type) {
    case "set-meta": {
      return withDirty(state, {
        ...state,
        meta: {
          ...state.meta,
          ...action.payload,
        },
      });
    }
    case "set-default-texts": {
      return withDirty(state, {
        ...state,
        defaultTexts: {
          ...state.defaultTexts,
          ...action.payload,
        },
      });
    }
    case "add-screen": {
      const nextId = generateScreenId(state.screens.map((s) => s.id));
      const template = action.payload?.template || "list";

      const newScreen = createScreenByTemplate(template, nextId);

      // 🎯 АВТОМАТИЧЕСКОЕ СВЯЗЫВАНИЕ С ПРЕДЫДУЩИМ ЭКРАНОМ
      let updatedScreens = [...state.screens, newScreen];
      
      // Если есть предыдущий экран и у него нет defaultNextScreenId, связываем с новым
      if (state.screens.length > 0) {
        const lastScreen = state.screens[state.screens.length - 1];
        if (!lastScreen.navigation?.defaultNextScreenId) {
          // Обновляем предыдущий экран, чтобы он указывал на новый
          updatedScreens = updatedScreens.map(screen => 
            screen.id === lastScreen.id 
              ? {
                  ...screen,
                  navigation: {
                    ...screen.navigation,
                    defaultNextScreenId: nextId,
                  }
                }
              : screen
          );
        }
      }

      return withDirty(state, {
        ...state,
        screens: updatedScreens,
        selectedScreenId: newScreen.id,
        meta: {
          ...state.meta,
          firstScreenId: state.meta.firstScreenId ?? newScreen.id,
        },
      });
    }
    case "insert-screen": {
      const { atIndex, template = "list" } = action.payload;
      const nextId = generateScreenId(state.screens.map((s) => s.id));
      const newScreen = createScreenByTemplate(template, nextId);

      // Вставляем экран на указанную позицию
      const updatedScreens = [...state.screens];
      updatedScreens.splice(atIndex, 0, newScreen);

      // 🎯 АВТОМАТИЧЕСКОЕ СВЯЗЫВАНИЕ
      // Обновляем навигацию предыдущего экрана (если есть)
      if (atIndex > 0) {
        const prevScreen = updatedScreens[atIndex - 1];
        const nextScreen = updatedScreens[atIndex + 1];
        
        // Если предыдущий экран указывал на следующий, перенаправляем через новый
        if (nextScreen && prevScreen.navigation?.defaultNextScreenId === nextScreen.id) {
          updatedScreens[atIndex - 1] = {
            ...prevScreen,
            navigation: {
              ...prevScreen.navigation,
              defaultNextScreenId: nextId,
            },
          };
          
          // Новый экран указывает на следующий
          updatedScreens[atIndex] = {
            ...newScreen,
            navigation: {
              ...newScreen.navigation,
              defaultNextScreenId: nextScreen.id,
            },
          };
        } else if (!prevScreen.navigation?.defaultNextScreenId) {
          // Если у предыдущего нет перехода, связываем с новым
          updatedScreens[atIndex - 1] = {
            ...prevScreen,
            navigation: {
              ...prevScreen.navigation,
              defaultNextScreenId: nextId,
            },
          };
        }
      }

      return withDirty(state, {
        ...state,
        screens: updatedScreens,
        selectedScreenId: newScreen.id,
        meta: {
          ...state.meta,
          firstScreenId: atIndex === 0 ? newScreen.id : state.meta.firstScreenId ?? newScreen.id,
        },
      });
    }
    case "remove-screen": {
      const filtered = state.screens.filter((screen) => screen.id !== action.payload.screenId);
      const selectedScreenId =
        state.selectedScreenId === action.payload.screenId ? filtered[0]?.id ?? null : state.selectedScreenId;

      const nextMeta = {
        ...state.meta,
        firstScreenId:
          state.meta.firstScreenId === action.payload.screenId
            ? filtered[0]?.id ?? null
            : state.meta.firstScreenId,
      };

      return withDirty(state, {
        ...state,
        screens: filtered,
        selectedScreenId,
        meta: nextMeta,
      });
    }
    case "update-screen": {
      const { screenId, screen } = action.payload;
      let nextSelectedScreenId = state.selectedScreenId;
      
      // Проверяем, меняется ли ID экрана
      const isIdChange = "id" in screen && screen.id !== screenId;
      const newScreenId = isIdChange ? screen.id! : screenId;

      let nextScreens = state.screens.map((current) =>
        current.id === screenId
          ? (() => {
                const nextScreen = {
                  ...current,
                  ...screen,
                  title: screen.title ? { ...current.title, ...screen.title } : current.title,
                  // Если subtitle явно передан в screen (даже если undefined), используем его
                  // Иначе сохраняем текущий subtitle
                  ...("subtitle" in screen
                    ? { subtitle: screen.subtitle }
                    : "subtitle" in current
                      ? { subtitle: current.subtitle }
                      : {}),
                  ...(current.template === "list" && "list" in screen && screen.list
                    ? {
                        list: {
                          ...(current as ListScreenDefinition).list,
                          ...screen.list,
                          options:
                            screen.list.options ??
                            (current as ListScreenDefinition).list.options,
                        },
                      }
                    : {}),
                } as BuilderScreen;

                if ("variants" in screen) {
                  if (Array.isArray(screen.variants) && screen.variants.length > 0) {
                    nextScreen.variants = screen.variants;
                  } else if ("variants" in nextScreen) {
                    delete (nextScreen as Partial<BuilderScreen>).variants;
                  }
                }

                if (state.selectedScreenId === current.id && nextScreen.id !== current.id) {
                  nextSelectedScreenId = nextScreen.id;
                }

                return nextScreen;
              })()
            : current
      );

      // Если изменился ID экрана, обновляем все ссылки на него
      if (isIdChange) {
        nextScreens = updateScreenIdReferences(nextScreens, screenId, newScreenId);
      }

      return withDirty(state, {
        ...state,
        screens: nextScreens,
        selectedScreenId: nextSelectedScreenId,
      });
    }
    case "reorder-screens": {
      const { fromIndex, toIndex } = action.payload;
      const previousScreens = state.screens;
      const newScreens = [...previousScreens];
      const [movedScreen] = newScreens.splice(fromIndex, 1);
      newScreens.splice(toIndex, 0, movedScreen);

      const previousSequentialNext = new Map<string, string | undefined>();
      const previousIndexMap = new Map<string, number>();
      const newSequentialNext = new Map<string, string | undefined>();

      previousScreens.forEach((screen, index) => {
        previousSequentialNext.set(screen.id, previousScreens[index + 1]?.id);
        previousIndexMap.set(screen.id, index);
      });

      newScreens.forEach((screen, index) => {
        newSequentialNext.set(screen.id, newScreens[index + 1]?.id);
      });

      const totalScreens = newScreens.length;

      const rewiredScreens = newScreens.map((screen, index) => {
        const prevIndex = previousIndexMap.get(screen.id);
        const prevSequential = previousSequentialNext.get(screen.id);
        const nextSequential = newScreens[index + 1]?.id;
        const navigation = screen.navigation;
        const hasRules = Boolean(navigation?.rules && navigation.rules.length > 0);

        let defaultNext = navigation?.defaultNextScreenId;
        if (!hasRules) {
          if (!defaultNext || defaultNext === prevSequential) {
            defaultNext = nextSequential;
          }
        } else if (defaultNext === prevSequential) {
          defaultNext = nextSequential;
        }

        const updatedNavigation = (() => {
          if ((navigation?.rules && navigation.rules.length > 0) || defaultNext) {
            // Обновляем nextScreenId в правилах навигации при reorder
            const updatedRules = navigation?.rules?.map(rule => {
              let updatedNextScreenId = rule.nextScreenId;
              
              // Обновляем ссылки если правило указывает на экран, который "следовал" за другим экраном
              // и эта последовательность изменилась
              for (const [screenId, oldNext] of previousSequentialNext.entries()) {
                const newNext = newSequentialNext.get(screenId);
                
                // Если правило указывало на экран, который раньше был "следующим" 
                // за каким-то экраном, но теперь следующим стал другой экран
                if (rule.nextScreenId === oldNext && newNext && oldNext !== newNext) {
                  updatedNextScreenId = newNext;
                  break;
                }
              }
              
              return {
                ...rule,
                nextScreenId: updatedNextScreenId
              };
            });

            return {
              ...(updatedRules ? { rules: updatedRules } : {}),
              ...(defaultNext ? { defaultNextScreenId: defaultNext } : {}),
            };
          }

          return undefined;
        })();

        let updatedHeader = screen.header;
        if (screen.header?.progress) {
          const progress = { ...screen.header.progress };
          const previousProgress = prevIndex !== undefined ? previousScreens[prevIndex]?.header?.progress : undefined;

          if (
            typeof progress.current === "number" &&
            prevIndex !== undefined &&
            (progress.current === prevIndex + 1 || previousProgress?.current === prevIndex + 1)
          ) {
            progress.current = index + 1;
          }

          if (typeof progress.total === "number") {
            const previousTotal = previousProgress?.total ?? progress.total;
            if (previousTotal === previousScreens.length) {
              progress.total = totalScreens;
            }
          }

          updatedHeader = {
            ...screen.header,
            progress,
          };
        }

        const nextScreen: BuilderScreen = {
          ...screen,
          ...(updatedHeader ? { header: updatedHeader } : {}),
        };

        if (updatedNavigation) {
          nextScreen.navigation = updatedNavigation;
        } else if ("navigation" in nextScreen) {
          delete nextScreen.navigation;
        }

        return nextScreen;
      });

      const nextMeta = {
        ...state.meta,
        firstScreenId: rewiredScreens[0]?.id,
      };

      const nextSelectedScreenId =
        movedScreen && state.selectedScreenId === movedScreen.id
          ? movedScreen.id
          : state.selectedScreenId;

      return withDirty(state, {
        ...state,
        screens: rewiredScreens,
        meta: nextMeta,
        selectedScreenId: nextSelectedScreenId,
      });
    }
    case "set-selected-screen": {
      return {
        ...state,
        selectedScreenId: action.payload.screenId,
      };
    }
    case "set-screens": {
      return withDirty(state, {
        ...state,
        screens: action.payload,
        selectedScreenId: action.payload[0]?.id ?? null,
        meta: {
          ...state.meta,
          firstScreenId: state.meta.firstScreenId ?? action.payload[0]?.id,
        },
      });
    }
    case "update-navigation": {
      const { screenId, navigation } = action.payload;
      return withDirty(state, {
        ...state,
        screens: state.screens.map((screen) =>
          screen.id === screenId
            ? {
                ...screen,
                navigation: {
                  defaultNextScreenId: navigation.defaultNextScreenId ?? undefined,
                  rules: navigation.rules ?? [],
                  isEndScreen: navigation.isEndScreen,
                  ...("onBackScreenId" in navigation
                    ? { onBackScreenId: navigation.onBackScreenId ?? undefined }
                    : {}),
                },
              }
            : screen
        ),
      });
    }
    case "reset": {
      return action.payload ? { ...action.payload, isDirty: false } : INITIAL_STATE;
    }
    default:
      return state;
  }
}
