import to from 'await-to-js';
import { LoginRequest, LoginResponse, MatrixError, createClient } from 'matrix-js-sdk';
import { useEffect } from 'react';
import { useNavigate } from 'react-router-dom';
import { ClientConfig, clientAllowedServer } from '../../../hooks/useClientConfig';
import { autoDiscovery, specVersions } from '../../../cs-api';
import { ErrorCode } from '../../../cs-errorcode';
import {
  deleteAfterLoginRedirectPath,
  getAfterLoginRedirectPath,
} from '../../afterLoginRedirectPath';
import { getHomePath } from '../../pathUtils';
import { setFallbackSession } from '../../../state/sessions';
import { getLocalStorageItem, setLocalStorageItem } from '../../../state/utils/atomWithLocalStorage';

export enum GetBaseUrlError {
  NotAllow = 'NotAllow',
  NotFound = 'NotFound',
}
export const factoryGetBaseUrl = (clientConfig: ClientConfig, server: string) => {
  const getBaseUrl = async (): Promise<string> => {
    if (!clientAllowedServer(clientConfig, server)) {
      throw new Error(GetBaseUrlError.NotAllow);
    }

    const [, discovery] = await to(autoDiscovery(fetch, server));

    let mxIdBaseUrl: string | undefined;
    const [, discoveryInfo] = discovery ?? [];

    if (discoveryInfo) {
      mxIdBaseUrl = discoveryInfo['m.homeserver'].base_url;
    }

    if (!mxIdBaseUrl) {
      throw new Error(GetBaseUrlError.NotFound);
    }
    const [, versions] = await to(specVersions(fetch, mxIdBaseUrl));
    if (!versions) {
      throw new Error(GetBaseUrlError.NotFound);
    }
    return mxIdBaseUrl;
  };
  return getBaseUrl;
};

export enum LoginError {
  ServerNotAllowed = 'ServerNotAllowed',
  InvalidServer = 'InvalidServer',
  Forbidden = 'Forbidden',
  UserDeactivated = 'UserDeactivated',
  InvalidRequest = 'InvalidRequest',
  RateLimited = 'RateLimited',
  Unknown = 'Unknown',
}

export type CustomLoginResponse = {
  baseUrl: string;
  response: LoginResponse;
};
export const login = async (
  serverBaseUrl: string | (() => Promise<string>),
  data: LoginRequest
): Promise<CustomLoginResponse> => {
  const [urlError, url] =
    typeof serverBaseUrl === 'function' ? await to(serverBaseUrl()) : [undefined, serverBaseUrl];
  if (urlError) {
    throw new MatrixError({
      errcode:
        urlError.message === GetBaseUrlError.NotAllow
          ? LoginError.ServerNotAllowed
          : LoginError.InvalidServer,
    });
  }

  const mx = createClient({ baseUrl: url });
  const [err, res] = await to<LoginResponse, MatrixError>(mx.loginRequest(data));

  if (err) {
    if (err.httpStatus === 400) {
      throw new MatrixError({
        errcode: LoginError.InvalidRequest,
      });
    }
    if (err.httpStatus === 429) {
      throw new MatrixError({
        errcode: LoginError.RateLimited,
      });
    }
    if (err.errcode === ErrorCode.M_USER_DEACTIVATED) {
      throw new MatrixError({
        errcode: LoginError.UserDeactivated,
      });
    }

    if (err.httpStatus === 403) {
      throw new MatrixError({
        errcode: LoginError.Forbidden,
      });
    }

    throw new MatrixError({
      errcode: LoginError.Unknown,
    });
  }
  return {
    baseUrl: url,
    response: res,
  };
};

export const useLoginComplete = (data?: CustomLoginResponse) => {
  const navigate = useNavigate();

  useEffect(() => {
    if (data) {
      const { response: loginRes, baseUrl: loginBaseUrl } = data;
      
      // Add session to sessions list
      const sessions = getLocalStorageItem('matrixSessions', []);
      
      const newSession = {
        baseUrl: loginBaseUrl,
        userId: loginRes.user_id,
        deviceId: loginRes.device_id,
        accessToken: loginRes.access_token,
        expiresInMs: loginRes.expires_in_ms,
        refreshToken: loginRes.refresh_token,
        fallbackSdkStores: false,
      };
      
      // Check if session already exists (by userId AND deviceId to avoid conflicts)
      const existingIndex = sessions.findIndex(
        (s: any) => s.userId === loginRes.user_id && s.deviceId === loginRes.device_id
      );
      if (existingIndex >= 0) {
        // Preserve fallbackSdkStores if it was set, but never enable it for new logins
        const oldSession = sessions[existingIndex];
        sessions[existingIndex] = {
          ...newSession,
          fallbackSdkStores: oldSession.fallbackSdkStores === true ? true : false,
        };
      } else {
        sessions.push(newSession);
      }
      
      setLocalStorageItem('matrixSessions', sessions);
      setLocalStorageItem('currentSessionUserId', loginRes.user_id);
      
      const afterLoginRedirectUrl = getAfterLoginRedirectPath();
      deleteAfterLoginRedirectPath();
      const targetPath = afterLoginRedirectUrl ?? getHomePath();
      console.log('[Login] Login complete, reloading to initialize client:', {
        targetPath,
        userId: loginRes.user_id,
        deviceId: loginRes.device_id,
        isMultiAccount: sessions.length > 1,
      });
      
      // Reload to the target path
      // For hash router, we need to use just the origin without /login pathname
      window.location.href = window.location.origin + '/#' + targetPath;
    }
  }, [data, navigate]);
};
