import { AppState, AppStateStatus } from 'react-native';
import EventSource from 'react-native-sse';

import { api, SSE_URL } from './api';
import { MessageCategory, SystemMessageType, SystemMessageExtraData, MessageSegment } from '../types/dto';
import { systemNotificationService } from './systemNotificationService';

export type WSMessageType =
  | 'chat'
  | 'message'
  | 'read'
  | 'typing'
  | 'recall'
  | 'notification'
  | 'announcement'
  | 'group_message'
  | 'group_typing'
  | 'group_notice'
  | 'group_mention'
  | 'group_read'
  | 'group_recall'
  | 'notice'
  | 'request'
  | 'meta'
  | 'private'
  | 'group'
  | 'follow'
  | 'like'
  | 'comment'
  | 'heartbeat';

export interface WSChatMessage {
  type: 'chat';
  conversation_id: string;
  id: string;
  sender_id: string;
  seq: number;
  segments?: MessageSegment[];
  created_at: string;
}

export interface WSReadMessage {
  type: 'read';
  conversation_id: string;
  user_id: string;
  seq: number;
}

export interface WSTypingMessage {
  type: 'typing';
  conversation_id: string;
  user_id: string;
  is_typing: boolean;
}

export interface WSRecallMessage {
  type: 'recall';
  conversation_id: string;
  message_id: string;
}

export interface WSNotificationMessage {
  type: 'notification';
  id: string;
  sender_id?: string;
  receiver_id?: string;
  content: string;
  category?: MessageCategory;
  system_type?: SystemMessageType;
  extra_data?: SystemMessageExtraData;
  created_at: string;
}

export interface WSAnnouncementMessage {
  type: 'announcement';
  id: string;
  sender_id?: string;
  receiver_id?: string;
  content: string;
  category?: MessageCategory;
  system_type?: SystemMessageType;
  extra_data?: SystemMessageExtraData;
  created_at: string;
}

export interface WSGroupMentionMessage {
  type: 'group_mention';
  group_id: string;
  conversation_id: string;
  message_id: string;
  from_user_id: string;
  content: string;
  mention_all: boolean;
  created_at: string;
}

export interface WSGroupChatMessage {
  type: 'group_message';
  conversation_id: string;
  group_id: string;
  id: string;
  sender_id: string;
  seq: number;
  segments?: MessageSegment[];
  created_at: string;
}

export interface WSGroupTypingMessage {
  type: 'group_typing';
  group_id: string;
  user_id: string;
  is_typing: boolean;
}

export type GroupNoticeType = 'member_join' | 'member_leave' | 'member_removed' | 'role_changed' | 'muted' | 'unmuted';

export interface WSGroupNoticeMessage {
  type: 'group_notice';
  notice_type: GroupNoticeType;
  group_id: string;
  data: {
    user_id?: string;
    operator_id?: string;
    role?: string;
    [key: string]: any;
  };
  timestamp: number;
  message_id?: string;
  seq?: number;
}

export interface WSGroupReadMessage {
  type: 'group_read';
  group_id: string;
  conversation_id: string;
  user_id: string;
  seq: number;
}

export interface WSGroupRecallMessage {
  type: 'group_recall';
  group_id: string;
  conversation_id: string;
  message_id: string;
}

export type WSMessage =
  | WSChatMessage
  | WSReadMessage
  | WSTypingMessage
  | WSRecallMessage
  | WSNotificationMessage
  | WSAnnouncementMessage
  | WSGroupChatMessage
  | WSGroupTypingMessage
  | WSGroupNoticeMessage
  | WSGroupMentionMessage
  | WSGroupReadMessage
  | WSGroupRecallMessage;

type MessageHandler<T extends WSMessage = WSMessage> = (message: T) => void;
type ConnectionHandler = () => void;

interface SSEEnvelope {
  event_id?: number;
  event?: string;
  ts?: number;
  payload?: any;
}

class SSEService {
  private source: EventSource | null = null;
  private isConnecting = false;
  private connected = false;
  private reconnectAttempts = 0;
  private maxReconnectAttempts = 20;
  private reconnectDelay = 3000;
  private reconnectTimer: NodeJS.Timeout | null = null;
  private connectionWatchdogTimer: NodeJS.Timeout | null = null;
  private readonly CONNECTION_IDLE_TIMEOUT_MS = 70000;
  private messageHandlers: Map<WSMessageType, MessageHandler[]> = new Map();
  private connectionHandlers: ConnectionHandler[] = [];
  private disconnectionHandlers: ConnectionHandler[] = [];
  private appStateSubscription: any = null;
  private lastAppState: AppStateStatus = 'active';
  private lastEventId = '';
  private lastActivityAt = 0;
  private shouldRun = false;

  private toSSEUrl(): string {
    return `${SSE_URL}?last_event_id=${encodeURIComponent(this.lastEventId)}`;
  }

  async connect(): Promise<boolean> {
    this.shouldRun = true;
    if (this.isConnecting || this.isConnected()) return true;
    this.isConnecting = true;
    try {
      const token = await api.getToken();
      if (!token) {
        this.isConnecting = false;
        return false;
      }
      const url = this.toSSEUrl();
      this.source = new EventSource(url, {
        headers: {
          Authorization: `Bearer ${token}`,
        },
        // Use one reconnect strategy only (manual), avoid library auto-reconnect fighting with us.
        pollingInterval: 0,
        // Be explicit to prevent platform-level short idle timeout behavior.
        timeout: this.CONNECTION_IDLE_TIMEOUT_MS,
      });

      this.source.addEventListener('open', () => {
        this.isConnecting = false;
        this.connected = true;
        this.reconnectAttempts = 0;
        this.markActivity();
        this.startConnectionWatchdog();
        this.connectionHandlers.forEach(h => h());
      });

      this.source.addEventListener('error', () => {
        this.handleDisconnected();
      });

      this.source.addEventListener('done' as any, () => {
        this.handleDisconnected();
      });

      this.source.addEventListener('close' as any, () => {
        this.handleDisconnected();
      });

      const events = ['chat_message', 'message_read', 'typing', 'system_notification', 'group_notice', 'message_recall', 'heartbeat'];
      events.forEach(eventName => {
        this.source?.addEventListener(eventName as any, (evt: any) => this.handleIncoming(eventName, evt));
      });

      return true;
    } catch {
      this.isConnecting = false;
      this.scheduleReconnect();
      return false;
    }
  }

  private handleIncoming(eventName: string, evt: any): void {
    this.markActivity();
    const rawData = typeof evt?.data === 'string' ? evt.data : '{}';
    const lastEventId = evt?.lastEventId;
    if (lastEventId) {
      this.lastEventId = String(lastEventId);
    }
    let payload: any = {};
    try {
      payload = JSON.parse(rawData);
    } catch {
      payload = {};
    }
    this.dispatchEvent(eventName, payload);
  }

  private dispatchEvent(eventName: string, payload: any): void {
    if (eventName === 'chat_message') {
      const detailType = payload?.detail_type || 'private';
      const m = payload?.message || payload;
      if (detailType === 'group') {
        const gm: WSGroupChatMessage = {
          type: 'group_message',
          conversation_id: m.conversation_id,
          group_id: String(m.group_id ?? ''),
          id: m.id,
          sender_id: m.sender_id,
          seq: Number(m.seq || 0),
          segments: m.segments || [],
          created_at: m.created_at || new Date().toISOString(),
        };
        this.emit('group_message', gm);
      } else {
        const cm: WSChatMessage = {
          type: 'chat',
          conversation_id: m.conversation_id,
          id: m.id,
          sender_id: m.sender_id,
          seq: Number(m.seq || 0),
          segments: m.segments || [],
          created_at: m.created_at || new Date().toISOString(),
        };
        this.emit('chat', cm);
      }
      return;
    }

    if (eventName === 'message_read') {
      const detailType = payload?.detail_type || 'private';
      if (detailType === 'group') {
        const m: WSGroupReadMessage = {
          type: 'group_read',
          group_id: String(payload.group_id ?? ''),
          conversation_id: payload.conversation_id,
          user_id: payload.user_id,
          seq: Number(payload.seq || 0),
        };
        this.emit('group_read', m);
      } else {
        const m: WSReadMessage = {
          type: 'read',
          conversation_id: payload.conversation_id,
          user_id: payload.user_id,
          seq: Number(payload.seq || 0),
        };
        this.emit('read', m);
      }
      return;
    }

    if (eventName === 'typing') {
      const detailType = payload?.detail_type || 'private';
      if (detailType === 'group') {
        const m: WSGroupTypingMessage = {
          type: 'group_typing',
          group_id: String(payload.group_id ?? ''),
          user_id: payload.user_id,
          is_typing: payload.is_typing !== false,
        };
        this.emit('group_typing', m);
      } else {
        const m: WSTypingMessage = {
          type: 'typing',
          conversation_id: payload.conversation_id,
          user_id: payload.user_id,
          is_typing: payload.is_typing !== false,
        };
        this.emit('typing', m);
      }
      return;
    }

    if (eventName === 'message_recall') {
      const detailType = payload?.detail_type || 'private';
      if (detailType === 'group') {
        const m: WSGroupRecallMessage = {
          type: 'group_recall',
          group_id: String(payload.group_id ?? ''),
          conversation_id: payload.conversation_id,
          message_id: payload.message_id,
        };
        this.emit('group_recall', m);
      } else {
        const m: WSRecallMessage = {
          type: 'recall',
          conversation_id: payload.conversation_id,
          message_id: payload.message_id,
        };
        this.emit('recall', m);
      }
      return;
    }

    if (eventName === 'group_notice') {
      const m: WSGroupNoticeMessage = {
        type: 'group_notice',
        notice_type: payload.notice_type,
        group_id: String(payload.group_id ?? ''),
        data: payload.data || {},
        timestamp: payload.timestamp || Date.now(),
        message_id: payload.message_id,
        seq: payload.seq,
      };
      this.emit('group_notice', m);
      return;
    }

    if (eventName === 'system_notification') {
      const m: WSNotificationMessage = {
        type: 'notification',
        id: String(payload.id ?? ''),
        content: payload.content || '',
        created_at: payload.created_at || new Date().toISOString(),
      };
      this.emit('notification', m);
      systemNotificationService.handleWSMessage(m as any).catch(() => {});
    }
  }

  private emit<T extends WSMessageType>(type: T, message: Extract<WSMessage, { type: T }>) {
    const handlers = this.messageHandlers.get(type) || [];
    handlers.forEach(h => h(message as WSMessage));
  }

  disconnect(): void {
    this.shouldRun = false;
    this.connected = false;
    this.isConnecting = false;
    if (this.source) {
      this.source.close();
      this.source = null;
    }
    this.stopConnectionWatchdog();
    this.stopReconnect();
  }

  private scheduleReconnect(): void {
    if (this.reconnectAttempts >= this.maxReconnectAttempts) return;
    this.stopReconnect();
    this.reconnectTimer = setTimeout(() => {
      this.reconnectAttempts += 1;
      this.connect();
    }, this.reconnectDelay);
  }

  private stopReconnect() {
    if (this.reconnectTimer) {
      clearTimeout(this.reconnectTimer);
      this.reconnectTimer = null;
    }
  }

  isConnected(): boolean {
    return this.connected;
  }

  on<T extends WSMessageType>(type: T, handler: MessageHandler<Extract<WSMessage, { type: T }>>): () => void {
    const list = this.messageHandlers.get(type) || [];
    list.push(handler as MessageHandler);
    this.messageHandlers.set(type, list);
    return () => {
      const current = this.messageHandlers.get(type) || [];
      const idx = current.indexOf(handler as MessageHandler);
      if (idx >= 0) current.splice(idx, 1);
    };
  }

  onConnect(handler: ConnectionHandler): () => void {
    this.connectionHandlers.push(handler);
    return () => {
      const i = this.connectionHandlers.indexOf(handler);
      if (i >= 0) this.connectionHandlers.splice(i, 1);
    };
  }

  onDisconnect(handler: ConnectionHandler): () => void {
    this.disconnectionHandlers.push(handler);
    return () => {
      const i = this.disconnectionHandlers.indexOf(handler);
      if (i >= 0) this.disconnectionHandlers.splice(i, 1);
    };
  }

  private setupAppStateListener(): void {
    if (this.appStateSubscription) return;
    this.lastAppState = AppState.currentState;
    this.appStateSubscription = AppState.addEventListener('change', (nextState: AppStateStatus) => {
      if (this.lastAppState.match(/inactive|background/) && nextState === 'active' && !this.isConnected()) {
        this.reconnectAttempts = 0;
        this.connect();
      }
      this.lastAppState = nextState;
    });
  }

  async start(): Promise<boolean> {
    this.setupAppStateListener();
    return this.connect();
  }

  stop(): void {
    if (this.appStateSubscription) {
      this.appStateSubscription.remove();
      this.appStateSubscription = null;
    }
    this.disconnect();
  }

  private markActivity(): void {
    this.lastActivityAt = Date.now();
  }

  private startConnectionWatchdog(): void {
    this.stopConnectionWatchdog();
    this.connectionWatchdogTimer = setInterval(() => {
      // During active app usage, if no SSE activity for too long, reconnect proactively.
      if (!this.connected || this.lastActivityAt <= 0) return;
      if (Date.now() - this.lastActivityAt > this.CONNECTION_IDLE_TIMEOUT_MS) {
        this.handleDisconnected();
      }
    }, 10000);
  }

  private stopConnectionWatchdog(): void {
    if (this.connectionWatchdogTimer) {
      clearInterval(this.connectionWatchdogTimer);
      this.connectionWatchdogTimer = null;
    }
  }

  private handleDisconnected(): void {
    const wasActive = this.connected || this.source != null || this.isConnecting;
    this.isConnecting = false;
    this.connected = false;
    if (this.source) {
      this.source.close();
      this.source = null;
    }
    this.stopConnectionWatchdog();
    if (wasActive) {
      this.disconnectionHandlers.forEach(h => h());
    }
    if (this.shouldRun) {
      this.scheduleReconnect();
    }
  }
}

export const sseService = new SSEService();
