import type { WebNotification, WebNotificationType, WebViewKey } from "../types";
import { isOptionalApiRouteMissing } from "./apiErrorUtils";
import { isRecord, isServerRequestError, serverRequest, writeStoredSession } from "./serverConnection";

interface CreateNotificationInput {
  type: WebNotificationType;
  title: string;
  description?: string;
  targetType?: string;
  targetId?: string;
  metadata?: Record<string, unknown>;
}

const NOTIFICATION_VIEW_BY_TARGET: Record<string, WebViewKey> = {
  task: "workbench",
  generation_task: "workbench",
  community_case: "login",
  asset: "assets",
  project: "canvas",
  draft: "workbench",
};

let notificationsRouteMissing = false;
let notificationsUnauthorized = false;

function toStringValue(value: unknown, fallback = ""): string {
  if (typeof value === "string") return value.trim() || fallback;
  if (typeof value === "number" && Number.isFinite(value)) return String(value);
  return fallback;
}

function normalizeType(value: unknown): WebNotificationType {
  const type = toStringValue(value);
  if (
    type === "task_completed" ||
    type === "task_failed" ||
    type === "review_pending" ||
    type === "review_passed" ||
    type === "review_rejected" ||
    type === "credits_low" ||
    type === "session_expired"
  ) {
    return type;
  }
  return "info";
}

function normalizeNotification(raw: unknown): WebNotification {
  const item = isRecord(raw) ? raw : {};
  const targetType = toStringValue(item.targetType ?? item.target_type) || null;
  const targetId = toStringValue(item.targetId ?? item.target_id) || undefined;
  const readAt = toStringValue(item.readAt ?? item.read_at) || null;
  return {
    id: toStringValue(item.id, `notice-${Date.now()}`),
    type: normalizeType(item.type),
    title: toStringValue(item.title, "通知"),
    description: toStringValue(item.description),
    createdAt: toStringValue(item.createdAt ?? item.created_at, new Date().toISOString()),
    isRead: Boolean(item.isRead ?? item.is_read ?? readAt),
    targetType,
    targetId,
    targetView: targetType ? NOTIFICATION_VIEW_BY_TARGET[targetType] : undefined,
    readAt,
    metadata: isRecord(item.metadata) ? item.metadata : {},
  };
}

function extractNotifications(payload: unknown): WebNotification[] {
  if (Array.isArray(payload)) return payload.map(normalizeNotification);
  if (!isRecord(payload)) return [];
  const rows = payload.notifications ?? payload.items;
  return Array.isArray(rows) ? rows.map(normalizeNotification) : [];
}

function isUnauthorized(error: unknown): boolean {
  return isServerRequestError(error) && (error.status === 401 || error.status === 403);
}

function handleUnauthorizedNotifications(): void {
  notificationsUnauthorized = true;
  writeStoredSession(null);
}

export const notificationClient = {
  async list(): Promise<WebNotification[]> {
    if (notificationsRouteMissing || notificationsUnauthorized) return [];
    try {
      return extractNotifications(await serverRequest<unknown>("notifications"));
    } catch (error) {
      if (isOptionalApiRouteMissing(error)) {
        notificationsRouteMissing = true;
        return [];
      }
      if (isUnauthorized(error)) {
        handleUnauthorizedNotifications();
        return [];
      }
      throw error;
    }
  },

  async create(input: CreateNotificationInput): Promise<WebNotification> {
    if (notificationsRouteMissing || notificationsUnauthorized) {
      return normalizeNotification({
        ...input,
        id: `local-notice-${Date.now()}`,
        createdAt: new Date().toISOString(),
      });
    }
    try {
      const payload = await serverRequest<{ notification: unknown }>("notifications", {
        method: "POST",
        body: input,
      });
      return normalizeNotification(payload.notification ?? payload);
    } catch (error) {
      if (isOptionalApiRouteMissing(error)) {
        notificationsRouteMissing = true;
        return normalizeNotification({
          ...input,
          id: `local-notice-${Date.now()}`,
          createdAt: new Date().toISOString(),
        });
      }
      if (isUnauthorized(error)) {
        handleUnauthorizedNotifications();
        return normalizeNotification({
          ...input,
          id: `local-notice-${Date.now()}`,
          createdAt: new Date().toISOString(),
        });
      }
      throw error;
    }
  },

  async markRead(id: string, isRead = true): Promise<void> {
    if (notificationsRouteMissing || notificationsUnauthorized) return;
    try {
      await serverRequest(`notifications/${id}/read`, {
        method: "PATCH",
        body: { isRead },
      });
    } catch (error) {
      if (isOptionalApiRouteMissing(error)) {
        notificationsRouteMissing = true;
        return;
      }
      if (isUnauthorized(error)) {
        handleUnauthorizedNotifications();
        return;
      }
      throw error;
    }
  },

  async markAllRead(): Promise<void> {
    if (notificationsRouteMissing || notificationsUnauthorized) return;
    try {
      await serverRequest("notifications/read-all", { method: "POST" });
    } catch (error) {
      if (isOptionalApiRouteMissing(error)) {
        notificationsRouteMissing = true;
        return;
      }
      if (isUnauthorized(error)) {
        handleUnauthorizedNotifications();
        return;
      }
      throw error;
    }
  },
};
