/* eslint-disable @typescript-eslint/no-unused-vars */
/**
 * Push Notification Service
 * Handles FCM (Firebase Cloud Messaging) for Android and APNS for iOS
 */

import { getRegisteredContainer } from '@bytelyst/cosmos';

// Device token record
interface DeviceToken {
  id: string;
  userId: string;
  productId: string;
  platform: 'ios' | 'android' | 'web';
  token: string;
  provider: 'fcm' | 'apns';
  createdAt: string;
  updatedAt: string;
  lastUsedAt?: string;
  isActive: boolean;
}

// Push payload
interface PushPayload {
  title: string;
  body: string;
  data?: Record<string, string>;
  imageUrl?: string;
  deepLink?: string;
  priority?: 'normal' | 'high';
  sound?: string;
  badge?: number;
}

function logPushWarning(message: string, extra?: Record<string, unknown>): void {
  process.stderr.write(JSON.stringify({ level: 'warn', module: 'push', message, ...extra }) + '\n');
}

function logPushError(message: string, error: unknown, extra?: Record<string, unknown>): void {
  process.stderr.write(
    JSON.stringify({
      level: 'error',
      module: 'push',
      message,
      error: error instanceof Error ? error.message : String(error),
      ...extra,
    }) + '\n'
  );
}

/**
 * Store or update device token for push notifications
 */
export async function registerDeviceToken(
  userId: string,
  productId: string,
  platform: 'ios' | 'android' | 'web',
  token: string
): Promise<void> {
  const container = getRegisteredContainer('devices');
  const id = `${userId}:${platform}:${token.substring(0, 16)}`;
  const now = new Date().toISOString();

  const provider = platform === 'ios' ? 'apns' : 'fcm';

  const deviceToken: DeviceToken = {
    id,
    userId,
    productId,
    platform,
    token,
    provider,
    createdAt: now,
    updatedAt: now,
    lastUsedAt: now,
    isActive: true,
  };

  await container.items.upsert(deviceToken);
}

/**
 * Deactivate device token (e.g., on logout or uninstall)
 */
export async function unregisterDeviceToken(userId: string, token: string): Promise<void> {
  const container = getRegisteredContainer('devices');

  // Find token by user and partial token match
  const query = 'SELECT * FROM c WHERE c.userId = @userId AND c.token = @token';
  const { resources } = await container.items
    .query<DeviceToken>({
      query,
      parameters: [
        { name: '@userId', value: userId },
        { name: '@token', value: token },
      ],
    })
    .fetchAll();

  for (const device of resources) {
    await container.items.upsert({
      ...device,
      isActive: false,
      updatedAt: new Date().toISOString(),
    });
  }
}

/**
 * Get active device tokens for users
 */
export async function getDeviceTokensForUsers(
  userIds: string[],
  platforms?: ('ios' | 'android' | 'web')[]
): Promise<DeviceToken[]> {
  const container = getRegisteredContainer('devices');

  const userIdList = userIds.map((id, i) => ({ name: `@userId${i}`, value: id }));
  const userIdParams = userIdList.map(p => p.name).join(', ');

  let query = `SELECT * FROM c WHERE c.userId IN (${userIdParams}) AND c.isActive = true`;
  const parameters = [...userIdList];

  if (platforms && platforms.length > 0) {
    const platformList = platforms.map((p, i) => ({ name: `@platform${i}`, value: p }));
    const platformParams = platformList.map(p => p.name).join(', ');
    query += ` AND c.platform IN (${platformParams})`;
    parameters.push(...platformList);
  }

  const { resources } = await container.items.query<DeviceToken>({ query, parameters }).fetchAll();

  return resources;
}

/**
 * Send push notification via FCM (Firebase Cloud Messaging)
 */
export async function sendFCM(
  tokens: string[],
  payload: PushPayload,
  productId: string
): Promise<{ success: string[]; failed: string[] }> {
  const results = { success: [] as string[], failed: [] as string[] };

  // Get FCM server key from environment
  const fcmKey = process.env.FCM_SERVER_KEY;
  if (!fcmKey) {
    logPushWarning('FCM_SERVER_KEY not configured');
    return results;
  }

  for (const token of tokens) {
    try {
      const response = await fetch('https://fcm.googleapis.com/fcm/send', {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
          Authorization: `key=${fcmKey}`,
        },
        body: JSON.stringify({
          to: token,
          notification: {
            title: payload.title,
            body: payload.body,
            image: payload.imageUrl,
            sound: payload.sound || 'default',
            badge: payload.badge,
          },
          data: {
            ...payload.data,
            deepLink: payload.deepLink,
            productId,
          },
          priority: payload.priority || 'normal',
        }),
      });

      if (response.ok) {
        results.success.push(token);
      } else {
        const error = await response.text();
        logPushError('FCM send failed', error, { tokenPrefix: token.substring(0, 16) });
        results.failed.push(token);
      }
    } catch (err) {
      logPushError('FCM send error', err, { tokenPrefix: token.substring(0, 16) });
      results.failed.push(token);
    }
  }

  return results;
}

/**
 * Send push notification via APNS (Apple Push Notification Service)
 */
export async function sendAPNS(
  tokens: string[],
  payload: PushPayload,
  productId: string
): Promise<{ success: string[]; failed: string[] }> {
  const results = { success: [] as string[], failed: [] as string[] };

  // APNS requires JWT-based authentication with p8 key
  // This is a simplified implementation
  const apnsKeyId = process.env.APNS_KEY_ID;
  const apnsTeamId = process.env.APNS_TEAM_ID;
  const apnsBundleId = process.env.APNS_BUNDLE_ID;
  const apnsPrivateKey = process.env.APNS_PRIVATE_KEY;

  if (!apnsKeyId || !apnsTeamId || !apnsBundleId || !apnsPrivateKey) {
    logPushWarning('APNS credentials not fully configured');
    return results;
  }

  // Import JWT library for APNS authentication
  const { SignJWT } = await import('jose');

  // Generate JWT token for APNS
  const privateKey = await importPKCS8(apnsPrivateKey, 'ES256');
  const jwt = await new SignJWT({})
    .setProtectedHeader({ alg: 'ES256', kid: apnsKeyId })
    .setIssuedAt()
    .setIssuer(apnsTeamId)
    .setExpirationTime('1h')
    .sign(privateKey);

  for (const token of tokens) {
    try {
      const response = await fetch(`https://api.push.apple.com/3/device/${token}`, {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
          Authorization: `bearer ${jwt}`,
          'apns-topic': apnsBundleId,
          'apns-priority': payload.priority === 'high' ? '10' : '5',
          'apns-push-type': 'alert',
        },
        body: JSON.stringify({
          aps: {
            alert: {
              title: payload.title,
              body: payload.body,
            },
            badge: payload.badge,
            sound: payload.sound || 'default',
            'mutable-content': 1,
          },
          ...payload.data,
          deepLink: payload.deepLink,
          productId,
        }),
      });

      if (response.ok) {
        results.success.push(token);
      } else {
        const error = await response.text();
        logPushError('APNS send failed', error, {
          status: response.status,
          tokenPrefix: token.substring(0, 16),
        });
        results.failed.push(token);

        // Handle invalid token (410 Gone)
        if (response.status === 410) {
          await deactivateToken(token);
        }
      }
    } catch (err) {
      logPushError('APNS send error', err, { tokenPrefix: token.substring(0, 16) });
      results.failed.push(token);
    }
  }

  return results;
}

/**
 * Send push notification to multiple users
 */
export async function sendPushNotification(
  userIds: string[],
  payload: PushPayload,
  productId: string,
  platforms?: ('ios' | 'android' | 'web')[]
): Promise<{
  totalTokens: number;
  fcmSuccess: number;
  fcmFailed: number;
  apnsSuccess: number;
  apnsFailed: number;
}> {
  const stats = {
    totalTokens: 0,
    fcmSuccess: 0,
    fcmFailed: 0,
    apnsSuccess: 0,
    apnsFailed: 0,
  };

  // Get device tokens
  const devices = await getDeviceTokensForUsers(userIds, platforms);
  stats.totalTokens = devices.length;

  if (devices.length === 0) {
    return stats;
  }

  // Group by provider
  const fcmTokens = devices.filter(d => d.provider === 'fcm').map(d => d.token);
  const apnsTokens = devices.filter(d => d.provider === 'apns').map(d => d.token);

  // Send via FCM
  if (fcmTokens.length > 0) {
    const fcmResults = await sendFCM(fcmTokens, payload, productId);
    stats.fcmSuccess = fcmResults.success.length;
    stats.fcmFailed = fcmResults.failed.length;
  }

  // Send via APNS
  if (apnsTokens.length > 0) {
    const apnsResults = await sendAPNS(apnsTokens, payload, productId);
    stats.apnsSuccess = apnsResults.success.length;
    stats.apnsFailed = apnsResults.failed.length;
  }

  return stats;
}

/**
 * Deactivate a token that has been rejected by APNS
 */
async function deactivateToken(token: string): Promise<void> {
  const container = getRegisteredContainer('devices');

  const query = 'SELECT * FROM c WHERE c.token = @token';
  const { resources } = await container.items
    .query<DeviceToken>({ query, parameters: [{ name: '@token', value: token }] })
    .fetchAll();

  for (const device of resources) {
    await container.items.upsert({
      ...device,
      isActive: false,
      updatedAt: new Date().toISOString(),
    });
  }
}

// Helper for importing PKCS8 key
async function importPKCS8(pem: string, alg: string): Promise<CryptoKey> {
  const pemHeader = '-----BEGIN PRIVATE KEY-----';
  const pemFooter = '-----END PRIVATE KEY-----';
  const pemContents = pem.replace(pemHeader, '').replace(pemFooter, '').replace(/\s/g, '');
  const binaryDer = Buffer.from(pemContents, 'base64');

  return crypto.subtle.importKey(
    'pkcs8',
    binaryDer,
    { name: 'ECDSA', namedCurve: 'P-256' },
    false,
    ['sign']
  );
}
