import type { EmitterSubscription } from 'react-native';
import { nativeModule, eventEmitter, isNativeModuleAvailable } from './FreshdeskModule';
import { FreshdeskEvents } from './events';
import { parseDiagnosticReport } from './diagnostics';
import { normalizeFreshdeskHost } from './utils/normalizeHost';
import type {
  FreshdeskConfig,
  TopicInfo,
  UserProperties,
  TicketProperties,
  EventProperties,
  UnreadCountEvent,
  UserStateEvent,
  LinkPressedEvent,
  UserCreatedEvent,
  ResetUserResult,
  ContentConfiguration,
  UserData,
  DiagnosticReport,
} from './types';

export { FreshdeskEvents } from './events';
export type { FreshdeskEventName } from './events';
export type {
  FreshdeskConfig,
  TopicInfo,
  UserProperties,
  TicketProperties,
  EventProperties,
  UnreadCountEvent,
  UserStateEvent,
  LinkPressedEvent,
  UserCreatedEvent,
  ResetUserResult,
  ContentConfiguration,
  HeaderContent,
  PlaceholderContent,
  PrivacyPolicyContent,
  ActionContent,
  ChannelResponseContent,
  ChannelResponseOnline,
  ChannelResponseTimeUnit,
  TicketFormContent,
  UserData,
  SDKVersion,
  DiagnosticStatus,
  DiagnosticCheck,
  DiagnosticReport,
} from './types';

export { UserState, FreshdeskErrorCode } from './types';
export { formatDiagnosticReport, parseDiagnosticReport } from './diagnostics';
export { normalizeFreshdeskHost, isBareFreshdeskHost } from './utils/normalizeHost';

/**
 * Initialize the Freshdesk SDK with configuration
 * Must be called before any other SDK methods
 * @param config SDK configuration
 */
export function initialize(config: FreshdeskConfig): Promise<void> {
  if (!isNativeModuleAvailable) {
    return Promise.reject(new Error('Freshdesk native module not available'));
  }
  return nativeModule.initialize({
    ...config,
    host: normalizeFreshdeskHost(config.host),
  });
}

/**
 * Open the Freshdesk support home screen
 */
export function openSupport(): Promise<void> {
  return nativeModule.openSupport();
}

/**
 * Open the Freshdesk knowledge base / FAQ screen
 */
export function openKnowledgeBase(): Promise<void> {
  return nativeModule.openKnowledgeBase();
}

/**
 * Open a specific support topic
 * @param topic Topic information (name and optional ID)
 */
export function openTopic(topic: TopicInfo): Promise<void> {
  return nativeModule.openTopic(topic.topicName, topic.topicId ?? '');
}

/**
 * Get the current unread message count
 *
 * Platform note: on Android this resolves a cached, broadcast-driven value and
 * is `0` until the first broadcast arrives after `initialize()`; on iOS it is
 * live. Prefer {@link addUnreadCountListener} for a value you can trust
 * immediately on both platforms. See PLATFORM_DIFFERENCES.md.
 *
 * @returns Promise resolving to the unread count
 */
export function getUnreadCount(): Promise<number> {
  return nativeModule.getUnreadCount();
}

/**
 * Track a user event for analytics and context
 * @param name Event name
 * @param properties Event properties
 */
export function trackEvent(name: string, properties?: EventProperties): Promise<void> {
  return nativeModule.trackEvent(name, properties ?? {});
}

/**
 * Set user properties (only for non-JWT enforced SDKs)
 * @param properties User properties
 */
export function setUserProperties(properties: UserProperties): Promise<void> {
  return nativeModule.setUserProperties(properties);
}

/**
 * Set ticket properties
 * @param properties Ticket properties
 */
export function setTicketProperties(properties: TicketProperties): Promise<void> {
  return nativeModule.setTicketProperties(properties);
}

/**
 * Authenticate or update user with JWT token
 * @param jwt JWT token
 */
export function authenticateAndUpdate(jwt: string): Promise<void> {
  return nativeModule.authenticateAndUpdate(jwt);
}

/**
 * Reset the current user and clear session data
 * Call this when user logs out
 *
 * Platform note: Android can resolve `{ success: false, error }` on a genuine
 * reset failure; iOS's native SDK has no failure callback for this operation
 * and always resolves `{ success: true }` once initialized (it only rejects
 * for the not-initialized usage error). Don't rely on `success: false` as a
 * cross-platform signal. See PLATFORM_DIFFERENCES.md.
 */
export function resetUser(): Promise<ResetUserResult> {
  return nativeModule.resetUser().then((r) => {
    const result: ResetUserResult = { success: r.success };
    if (r.message) {
      result.message = r.message;
    }
    if (r.error) {
      result.error = r.error;
    }
    return result;
  });
}

/**
 * Dismiss any open Freshdesk views
 */
export function dismiss(): Promise<void> {
  return nativeModule.dismiss();
}

/**
 * Set a custom link handler
 * When a link is pressed in the SDK, an event will be emitted
 * @param handler Callback function to handle link presses
 * @returns EmitterSubscription to remove the listener
 */
export function setLinkHandler(
  handler: (event: LinkPressedEvent) => void
): EmitterSubscription | null {
  if (!eventEmitter) {
    console.warn('Freshdesk event emitter not available');
    return null;
  }

  // Enable link handler in native module
  nativeModule.setLinkHandlerEnabled(true).catch((err: Error) => {
    console.warn('Failed to enable link handler:', err);
  });

  return eventEmitter.addListener(FreshdeskEvents.ON_LINK_PRESSED, handler);
}

/**
 * Add a listener for unread count changes
 * @param listener Callback function receiving the unread count
 * @returns EmitterSubscription to remove the listener
 */
export function addUnreadCountListener(
  listener: (event: UnreadCountEvent) => void
): EmitterSubscription | null {
  if (!eventEmitter) {
    console.warn('Freshdesk event emitter not available');
    return null;
  }
  return eventEmitter.addListener(FreshdeskEvents.UNREAD_COUNT_CHANGED, listener);
}

/**
 * Add a listener for user state changes
 * @param listener Callback function receiving the user state
 * @returns EmitterSubscription to remove the listener
 */
export function addUserStateListener(
  listener: (event: UserStateEvent) => void
): EmitterSubscription | null {
  if (!eventEmitter) {
    console.warn('Freshdesk event emitter not available');
    return null;
  }
  return eventEmitter.addListener(FreshdeskEvents.USER_STATE_CHANGED, listener);
}

/**
 * Add a listener for user creation events
 * @param listener Callback function receiving user creation event
 * @returns EmitterSubscription to remove the listener
 */
export function addUserCreatedListener(
  listener: (event: UserCreatedEvent) => void
): EmitterSubscription | null {
  if (!eventEmitter) {
    console.warn('Freshdesk event emitter not available');
    return null;
  }
  return eventEmitter.addListener(FreshdeskEvents.USER_CREATED, listener);
}

/**
 * Remove all Freshdesk event listeners
 * Call this when cleaning up (e.g., on component unmount)
 */
export function removeAllListeners(): void {
  if (!eventEmitter) {
    return;
  }
  eventEmitter.removeAllListeners(FreshdeskEvents.UNREAD_COUNT_CHANGED);
  eventEmitter.removeAllListeners(FreshdeskEvents.USER_STATE_CHANGED);
  eventEmitter.removeAllListeners(FreshdeskEvents.USER_CREATED);
  eventEmitter.removeAllListeners(FreshdeskEvents.ON_LINK_PRESSED);

  // Disable link handler
  nativeModule.setLinkHandlerEnabled(false).catch(() => {
    // Ignore errors when disabling
  });
}

/**
 * Get the SDK version string
 * @returns Promise resolving to SDK version (e.g., "1.0.1")
 */
export function getSDKVersion(): Promise<string> {
  if (!isNativeModuleAvailable) {
    return Promise.resolve('unknown');
  }
  return nativeModule.getSDKVersion();
}

/**
 * Get the current user information from the SDK
 * @returns Promise resolving to user data object
 */
export async function getUser(): Promise<UserData> {
  if (!isNativeModuleAvailable) {
    return Promise.resolve({});
  }
  const userJson = await nativeModule.getUser();
  try {
    return JSON.parse(userJson) as UserData;
  } catch {
    return {};
  }
}

/**
 * Set content configuration to customize SDK UI text
 * @param config Content configuration for headers, placeholders, etc.
 */
export function setContentConfiguration(config: ContentConfiguration): Promise<void> {
  if (!isNativeModuleAvailable) {
    return Promise.reject(new Error('Freshdesk native module not available'));
  }
  const configString = JSON.stringify(config);
  return nativeModule.setContentConfiguration(configString);
}

/**
 * Enable or disable Freshdesk SDK debug logs
 *
 * Platform note: reliable at runtime on iOS only. On Android, debug logging
 * can only be set at `initialize()` time (`debugMode: true`); calling this
 * afterward is a no-op there. See PLATFORM_DIFFERENCES.md.
 *
 * @param enabled Whether debug logging should be enabled
 */
export function enableDebugLogs(enabled: boolean): Promise<void> {
  if (!isNativeModuleAvailable) {
    return Promise.reject(new Error('Freshdesk native module not available'));
  }
  return nativeModule.enableDebugLogs(enabled);
}

/**
 * Run Freshdesk SDK diagnostics and return a structured report
 */
export async function runDiagnostics(): Promise<DiagnosticReport> {
  if (!isNativeModuleAvailable) {
    return parseDiagnosticReport(await nativeModule.runDiagnostics());
  }
  const raw = await nativeModule.runDiagnostics();
  return parseDiagnosticReport(raw);
}

/**
 * Freshdesk SDK object for convenient access
 */
export const FreshdeskSDK = {
  initialize,
  openSupport,
  openKnowledgeBase,
  openTopic,
  getUnreadCount,
  trackEvent,
  setUserProperties,
  setTicketProperties,
  authenticateAndUpdate,
  resetUser,
  dismiss,
  setLinkHandler,
  addUnreadCountListener,
  addUserStateListener,
  addUserCreatedListener,
  removeAllListeners,
  getSDKVersion,
  getUser,
  setContentConfiguration,
  enableDebugLogs,
  runDiagnostics,
  events: FreshdeskEvents,
};

export default FreshdeskSDK;
