// XKModule 및 관련 타입 정의
declare global {
  interface Window {
    XKModule: any;
    XKConfigMobile: {
      maxInputSize: number;
      rsaPublicKey?: {
        n: string;
        e: string;
      };
    };
    RSASetPublic: (N: string, E: string) => void;
    RSAEncrypt: (text: string) => string;
  }
}

export interface XKSessionInfo {
  sessionId: string;
  input: string;
  secToken: string;
}

export interface XKModuleInstance {
  initialize: (options: any) => number;
  open: () => void;
  close: () => void;
  isOpen: () => boolean;
  clear: () => void;
  destroy: () => void;
  get_input: () => string;
  get_sessionInfo: () => XKSessionInfo;
  setRSAPublicKey: (n: string, e: string) => void;
}

export type KeyType = 'qwertysmart' | 'number';
export type ViewType = 'half' | 'normal';
export type NumberKeyRowCount = 2 | 3 | 4;

export interface XKeypadOptions {
  keyType?: KeyType;
  viewType?: ViewType;
  numberKeyRowCount?: NumberKeyRowCount;
  maxInputSize?: number;
  width?: number;
  position?: { top?: number | null; left?: number | null };
  closeDelay?: number;
  autoKeyResize?: boolean;
  isE2E?: boolean;
  onlyMobile?: boolean;
  hasPressEffect?: boolean;
  useModal?: boolean;
  useOverlay?: boolean; // 오버레이 사용 여부
  onInputChange?: (newLength: number) => void;
  onKeypadClose?: () => void;
}

export interface XKeypadResult {
  type: 'E2E' | 'Plain';
  sessionId?: string;
  encryptedInput?: string;
  secToken?: string;
  plainText?: string;
  rsaEncrypted?: string;
}

export interface RSAConfig {
  modulus: string;
  exponent: string;
}

// 기본 RSA 키 설정
const DEFAULT_RSA_CONFIG: RSAConfig = {
  modulus: "C4F7B39E2E93DB19C016C7A0C1C05B028A1D57CB9B91E13F5B7353F8FB5AC6CE6BE31ABEB8E8F7AD18B90C08F4EBC011A6A8FCE614EA879ED5B96296B969CE92923BC9BAD6FD87F00E08F529F93010EA77E40937BDAC1C866E79ACE2F2822A3ECD982F90532D5301CF90D9BF89E953A0593AB6C5F31E99B690DD582FB85F85A9",
  exponent: "10001"
};

export class XKeypadManager {
  private static instance: XKeypadManager;
  private scriptsLoaded: boolean = false;
  private loadingPromise: Promise<void> | null = null;
  private xkModules: Map<string, XKModuleInstance> = new Map();
  private rsaConfig: RSAConfig;

  private constructor(rsaConfig?: RSAConfig) {
    this.rsaConfig = rsaConfig || DEFAULT_RSA_CONFIG;
  }

  public static getInstance(rsaConfig?: RSAConfig): XKeypadManager {
    if (!XKeypadManager.instance) {
      XKeypadManager.instance = new XKeypadManager(rsaConfig);
    }
    return XKeypadManager.instance;
  }

  /**
   * XKeypad 스크립트를 로드합니다.
   */
  public async loadScripts(): Promise<void> {
    // 이미 로드 중이거나 로드됨
    if (this.loadingPromise) {
      return this.loadingPromise;
    }

    if (this.scriptsLoaded && window.XKModule) {
      return Promise.resolve();
    }

    this.loadingPromise = this.loadScriptsInternal();
    await this.loadingPromise;
    this.scriptsLoaded = true;
  }

  private async loadScriptsInternal(): Promise<void> {
    try {
      // Check if scripts are already loaded
      if (window.XKModule) {
        this.scriptsLoaded = true;
        // Set RSA keys after scripts are loaded
        if (this.rsaConfig && window.RSASetPublic) {
          window.RSASetPublic(this.rsaConfig.modulus, this.rsaConfig.exponent);
        }
        return;
      }

      // Load scripts in order
      const scripts = [
        '/src/shared/ui/assets/js/xkeypad_config.js',
        '/src/shared/ui/assets/js/rsa_crypto.js',
        '/src/shared/ui/assets/js/xkeypad.js'
      ];

      for (const src of scripts) {
        await this.loadScript(src);
      }

      // Add CSS files
      this.loadCSS('/src/shared/ui/assets/css/xkeypad-modal.css', 'xkeypad-modal-css');
      this.loadCSS('/src/shared/ui/assets/css/xkeypad.css', 'xkeypad-css');

      // Set RSA keys after scripts are loaded
      if (this.rsaConfig && window.RSASetPublic) {
        window.RSASetPublic(this.rsaConfig.modulus, this.rsaConfig.exponent);
      }

    } catch (error) {
      console.error('Failed to load XKeypad scripts:', error);
      throw error;
    }
  }

  private loadScript(src: string): Promise<void> {
    return new Promise((resolve, reject) => {
      const script = document.createElement('script');
      script.src = src;
      script.charset = 'utf-8';
      script.onload = () => resolve();
      script.onerror = () => reject(new Error(`Failed to load script: ${src}`));
      document.head.appendChild(script);
    });
  }

  private loadCSS(href: string, id: string): void {
    if (!document.getElementById(id)) {
      const link = document.createElement('link');
      link.id = id;
      link.rel = 'stylesheet';
      link.href = href;
      document.head.appendChild(link);
    }
  }

  /**
   * 새로운 XKModule 인스턴스를 생성합니다.
   */
  public createModule(id: string): XKModuleInstance | null {
    if (!this.scriptsLoaded || !window.XKModule) {
      console.error('XKeypad scripts not loaded');
      return null;
    }

    const module = new window.XKModule();
    this.xkModules.set(id, module);
    return module;
  }

  /**
   * 기존 XKModule 인스턴스를 가져옵니다.
   */
  public getModule(id: string): XKModuleInstance | null {
    return this.xkModules.get(id) || null;
  }

  /**
   * XKModule 인스턴스를 제거합니다.
   */
  public removeModule(id: string): void {
    const module = this.xkModules.get(id);
    if (module) {
      if (module.isOpen()) {
        module.close();
      }
      this.xkModules.delete(id);
    }
  }

  /**
   * 모든 키패드를 닫습니다.
   */
  public closeAllKeypads(): void {
    this.xkModules.forEach(module => {
      if (module && module.isOpen()) {
        module.close();
      }
    });
  }

  /**
   * RSA 공개키를 설정합니다.
   */
  public setRSAPublicKey(modulus: string, exponent: string): void {
    this.rsaConfig = { modulus, exponent };

    // RSASetPublic이 있는지 확인하고 설정
    if (window.RSASetPublic && typeof window.RSASetPublic === 'function') {
      try {
        window.RSASetPublic(modulus, exponent);
      } catch (error) {
        console.error('Failed to set RSA public key:', error);
      }
    }

    // Update all existing modules
    this.xkModules.forEach(module => {
      if (module && typeof module.setRSAPublicKey === 'function') {
        module.setRSAPublicKey(modulus, exponent);
      }
    });
  }

  /**
   * RSA로 텍스트를 암호화합니다.
   */
  public encryptRSA(text: string): string | null {
    if (!text) {
      return null;
    }

    // RSAEncrypt 함수가 있는지 확인
    if (!window.RSAEncrypt || typeof window.RSAEncrypt !== 'function') {
      console.error('RSA encryption not available');
      return null;
    }

    try {
      // RSA 키가 설정되어 있는지 확인
      if (this.rsaConfig && window.RSASetPublic) {
        window.RSASetPublic(this.rsaConfig.modulus, this.rsaConfig.exponent);
      }

      return window.RSAEncrypt(text);
    } catch (error) {
      console.error('RSA encryption failed:', error);
      return null;
    }
  }
}

/**
 * XKeypad 인스턴스를 관리하는 클래스
 */
export class XKeypad {
  private manager: XKeypadManager;
  private module: XKModuleInstance | null = null;
  private moduleId: string;
  private options: XKeypadOptions;
  private containerName: string;
  private inputElement: HTMLInputElement | null = null;
  private overlayElement: HTMLDivElement | null = null;

  constructor(
    moduleId: string,
    options: XKeypadOptions = {},
    rsaConfig?: RSAConfig
  ) {
    this.manager = XKeypadManager.getInstance(rsaConfig);
    this.moduleId = moduleId;
    this.options = {
      keyType: 'qwertysmart',
      viewType: 'half',
      numberKeyRowCount: 3,
      maxInputSize: 50,
      width: 100,
      closeDelay: 300,
      autoKeyResize: false,
      isE2E: false,
      onlyMobile: false,
      hasPressEffect: true,
      useModal: false,
      useOverlay: true, // 기본값: 오버레이 사용
      ...options
    };
    this.containerName = this.options.useModal
      ? `xk-modal-${moduleId}`
      : `xk-pad-${moduleId}`;
  }

  /**
   * 키패드를 초기화합니다.
   */
  public async initialize(inputElement: HTMLInputElement): Promise<number> {
    // 스크립트 로드
    await this.manager.loadScripts();

    // 입력 요소 저장
    this.inputElement = inputElement;

    // 모듈 생성
    this.module = this.manager.createModule(this.moduleId);
    if (!this.module) {
      return -1;
    }

    // RSA 키 설정 (E2E가 아닌 경우)
    if (!this.options.isE2E) {
      const rsaConfig = (this.manager as any).rsaConfig;
      if (rsaConfig && typeof this.module.setRSAPublicKey === 'function') {
        this.module.setRSAPublicKey(rsaConfig.modulus, rsaConfig.exponent);
      }
    }

    // 모달 컨테이너 생성 (필요시)
    if (this.options.useModal) {
      this.createModalContainer();
    }

    // 키패드 초기화
    const result = this.module.initialize({
      name: this.containerName,
      editBox: inputElement,
      keyType: this.options.keyType,
      maxInputSize: this.options.maxInputSize,
      width: this.options.width,
      position: {
        top: this.options.useModal ? 0 : (this.options.position?.top ?? null),
        left: this.options.position?.left ?? null
      },
      viewType: this.options.useModal ? 'normal' : this.options.viewType,
      numberKeyRowCount: this.options.numberKeyRowCount,
      closeDelay: this.options.closeDelay,
      autoKeyResize: this.options.autoKeyResize,
      isE2E: this.options.isE2E,
      onlyMobile: this.options.onlyMobile,
      hasPressEffect: this.options.hasPressEffect,
      onInputChange: this.options.onInputChange,
      onKeypadClose: () => {
        if (this.options.useModal) {
          this.hideModal();
        }
        if (this.options.useOverlay && !this.options.useModal) {
          this.hideOverlay();
        }
        if (this.options.onKeypadClose) {
          this.options.onKeypadClose();
        }
      }
    });

    if (result === 0) {
      if (this.options.useModal) {
        this.showModal();
      } else if (this.options.useOverlay) {
        this.showOverlay();
      }
    }

    return result;
  }

  /**
   * 키패드를 엽니다.
   */
  public open(): void {
    if (this.module && !this.module.isOpen()) {
      this.module.open();
      if (this.options.useModal) {
        this.showModal();
      } else if (this.options.useOverlay) {
        this.showOverlay();
      }
    }
  }

  /**
   * 키패드를 닫습니다.
   */
  public close(): void {
    if (this.module && this.module.isOpen()) {
      this.module.close();
      if (this.options.useModal) {
        this.hideModal();
      } else if (this.options.useOverlay) {
        this.hideOverlay();
      }
    }
  }

  /**
   * 키패드가 열려있는지 확인합니다.
   */
  public isOpen(): boolean {
    return this.module ? this.module.isOpen() : false;
  }

  /**
   * 입력을 초기화합니다.
   */
  public clear(): void {
    if (this.module) {
      this.module.clear();
    }
    if (this.inputElement) {
      this.inputElement.value = '';
    }
  }

  /**
   * 키패드를 파괴합니다.
   */
  public destroy(): void {
    if (this.module) {
      if (this.module.isOpen()) {
        this.module.close();
      }
      this.module.destroy();
    }
    this.manager.removeModule(this.moduleId);

    // Remove modal container if exists
    if (this.options.useModal) {
      const wrapper = document.getElementById(`${this.containerName}-wrapper`);
      if (wrapper) {
        wrapper.remove();
      }
    }

    // Remove overlay if exists
    if (this.options.useOverlay && !this.options.useModal) {
      this.hideOverlay();
    }
  }

  /**
   * 입력 값을 가져옵니다.
   */
  public getValue(): XKeypadResult | null {
    if (!this.module) return null;

    const result: XKeypadResult = {} as XKeypadResult;

    if (this.options.isE2E) {
      const sessionInfo = this.module.get_sessionInfo();
      result.type = 'E2E';
      result.sessionId = sessionInfo.sessionId;
      result.encryptedInput = sessionInfo.input;
      result.secToken = sessionInfo.secToken;
    } else {
      const plainText = this.module.get_input();
      result.type = 'Plain';
      result.plainText = plainText;

      // RSA encryption if available
      if (plainText) {
        const encrypted = this.manager.encryptRSA(plainText);
        if (encrypted) {
          result.rsaEncrypted = encrypted;
        }
      }
    }

    return result;
  }

  /**
   * 평문 입력 값을 가져옵니다.
   */
  public getPlainText(): string {
    if (!this.module) return '';
    return this.module.get_input();
  }

  /**
   * 세션 정보를 가져옵니다. (E2E 모드에서만 유효)
   */
  public getSessionInfo(): XKSessionInfo | null {
    if (!this.module || !this.options.isE2E) return null;
    return this.module.get_sessionInfo();
  }

  // Modal helper methods
  private createModalContainer(): void {
    const name = this.containerName;

    // Remove existing container if any
    const existing = document.getElementById(`${name}-wrapper`);
    if (existing) {
      existing.remove();
    }

    // Create modal structure
    const wrapper = document.createElement('div');
    wrapper.id = `${name}-wrapper`;
    wrapper.className = 'xkeypad-modal-wrapper';
    wrapper.style.display = 'none';

    const overlay = document.createElement('div');
    overlay.className = 'xkeypad-modal-overlay';
    overlay.onclick = () => this.close();

    const container = document.createElement('div');
    container.className = 'xkeypad-modal-container';

    const content = document.createElement('div');
    content.id = name;
    content.className = 'xkeypad-modal-content';

    container.appendChild(content);
    wrapper.appendChild(overlay);
    wrapper.appendChild(container);
    document.body.appendChild(wrapper);
  }

  private showModal(): void {
    const wrapper = document.getElementById(`${this.containerName}-wrapper`);
    if (wrapper) {
      wrapper.style.display = 'block';
      setTimeout(() => {
        wrapper.classList.add('show');
      }, 10);
    }
  }

  private hideModal(): void {
    const wrapper = document.getElementById(`${this.containerName}-wrapper`);
    if (wrapper) {
      wrapper.classList.remove('show');
      setTimeout(() => {
        wrapper.style.display = 'none';
      }, 300);
    }
  }

  // Overlay methods for non-modal mode
  private showOverlay(): void {
    // Remove existing overlay if any
    this.hideOverlay();

    // Create overlay
    this.overlayElement = document.createElement('div');
    this.overlayElement.id = `xkeypad-overlay-${this.moduleId}`;
    this.overlayElement.style.cssText = `
      position: fixed;
      top: 0;
      left: 0;
      right: 0;
      bottom: 0;
      background-color: rgba(0, 0, 0, 0.5);
      z-index: 9998;
      transition: opacity 0.3s ease;
      opacity: 0;
    `;

    // Add click handler to close keypad
    this.overlayElement.onclick = () => this.close();

    // Append to body
    document.body.appendChild(this.overlayElement);

    // Trigger transition
    setTimeout(() => {
      if (this.overlayElement) {
        this.overlayElement.style.opacity = '1';
      }
    }, 10);

    // Adjust keypad z-index to be above overlay
    const keypadContainer = document.getElementById(this.containerName);
    if (keypadContainer) {
      keypadContainer.style.zIndex = '9999';
    }
  }

  private hideOverlay(): void {
    if (this.overlayElement) {
      this.overlayElement.style.opacity = '0';
      setTimeout(() => {
        if (this.overlayElement && this.overlayElement.parentNode) {
          this.overlayElement.parentNode.removeChild(this.overlayElement);
          this.overlayElement = null;
        }
      }, 300);
    }
  }
}

/**
 * 간편 사용을 위한 헬퍼 함수들
 */

/**
 * 비밀번호 입력용 키패드를 생성합니다.
 */
export function createPasswordKeypad(
  inputElement: HTMLInputElement,
  options?: Partial<XKeypadOptions>,
  rsaConfig?: RSAConfig
): XKeypad {
  return new XKeypad('password', {
    keyType: 'qwertysmart',
    maxInputSize: 16,
    ...options
  }, rsaConfig);
}

/**
 * PIN 입력용 키패드를 생성합니다.
 */
export function createPinKeypad(
  inputElement: HTMLInputElement,
  options?: Partial<XKeypadOptions>,
  rsaConfig?: RSAConfig
): XKeypad {
  return new XKeypad('pin', {
    keyType: 'number',
    maxInputSize: 6,
    ...options
  }, rsaConfig);
}

/**
 * 카드번호 입력용 키패드를 생성합니다.
 */
export function createCardKeypad(
  inputElement: HTMLInputElement,
  options?: Partial<XKeypadOptions>,
  rsaConfig?: RSAConfig
): XKeypad {
  return new XKeypad('card', {
    keyType: 'number',
    maxInputSize: 16,
    numberKeyRowCount: 4,
    ...options
  }, rsaConfig);
}

// Default export
export default XKeypad;