'use client';

import { createContext, useContext, useState, useCallback, ReactNode, useEffect } from 'react';
import { fetchWithAuth } from './auth';

const API = process.env.NEXT_PUBLIC_API_URL ?? '';

async function fetchSources(workspaceId: string): Promise<any[]> {
  try {
    const res = await fetchWithAuth(`${API}/api/workspace/${encodeURIComponent(workspaceId)}/sources`);
    if (!res.ok) return [];
    return (await res.json()).sources ?? [];
  } catch { return []; }
}

async function saveSources(workspaceId: string, sources: any[]): Promise<void> {
  try {
    await fetchWithAuth(`${API}/api/workspace/${encodeURIComponent(workspaceId)}/sources`, {
      method: 'PUT',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ sources }),
    });
  } catch (e) { console.warn('saveSources failed:', e); }
}

async function fetchArtifacts(workspaceId: string): Promise<any[]> {
  try {
    const res = await fetchWithAuth(`${API}/api/workspace/${encodeURIComponent(workspaceId)}/artifacts`);
    if (!res.ok) return [];
    return (await res.json()).artifacts ?? [];
  } catch { return []; }
}

async function saveArtifacts(workspaceId: string, artifacts: any[]): Promise<void> {
  try {
    await fetchWithAuth(`${API}/api/workspace/${encodeURIComponent(workspaceId)}/artifacts`, {
      method: 'PUT',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ artifacts }),
    });
  } catch (e) { console.warn('saveArtifacts failed:', e); }
}


// ---------------------------------------------------------------------------
// File tree types
// ---------------------------------------------------------------------------

export interface WorkspaceFile {
  name: string;
  path: string;
  type: 'file' | 'directory';
  children?: WorkspaceFile[];
  size?: string;
  sizeBytes?: number;
}

// ---------------------------------------------------------------------------
// Source types
// ---------------------------------------------------------------------------

export type SourceType = 'jira_issue' | 'alertstore_alert' | 'git_repo' | 'cluster_meta' | 'user_meta' | 'skill';

const PREPARED_SOURCE_TYPES: ReadonlySet<SourceType> = new Set(['jira_issue', 'alertstore_alert']);

export function isPreparedSourceType(type: string | null | undefined): type is SourceType {
  return !!type && PREPARED_SOURCE_TYPES.has(type as SourceType);
}

export function isPreparedSource(source: Pick<Source, 'type'>): boolean {
  return isPreparedSourceType(source.type);
}

export interface Source {
  id: string;
  type: SourceType;
  name: string;
  description?: string;
  readonly?: boolean;
  /** For prepared Jira/Alertstore sources, the associated source ID */
  issueId?: string;
  /** Relative directory path where this issue's files live (e.g. "issues/O11Y-1956041") */
  issueDir?: string;
  /** Additional metadata */
  meta?: Record<string, unknown>;
  createdAt: Date;
  /**
   * If true, this source is shown in the UI for context/awareness only.
   * It does not drive any backend connections in the current implementation.
   */
  displayOnly?: boolean;
  /** Loading state for sources that require backend preparation */
  status?: 'loading' | 'ready' | 'error';
  statusMessage?: string;
  /** Backend task ID for polling preparation status */
  taskId?: string;
  /** Resolved O11Y alert rule file path after preparation */
  rule?: string;
  /** Resolved alert expr after preparation */
  ruleExpr?: string;
  /** Resolved alert for duration after preparation */
  ruleFor?: string;
}

// ---------------------------------------------------------------------------
// Artifact types
// ---------------------------------------------------------------------------

export type ArtifactType = 'report' | 'chart' | 'summary' | 'timeline' | 'note';

export interface Artifact {
  id: string;
  type: ArtifactType;
  title: string;
  description?: string;
  /** Source ID that generated this artifact */
  sourceId?: string;
  /** Jira issue ID that introduced this artifact (e.g. "O11Y-1956041", "TCOC-12345", "TCID-67890", "ST-13579") */
  issueId?: string;
  /** Relative path prefix for this artifact's data files (e.g. "issues/O11Y-1956041") */
  issueDir?: string;
  createdAt: Date;
  /** Additional data for rendering */
  data?: Record<string, unknown>;
}

// ---------------------------------------------------------------------------
// Source type metadata
// ---------------------------------------------------------------------------

export type SourceTypeMeta = { label: string; icon: string; color: string; description: string };

export const SOURCE_TYPE_META: Record<SourceType, SourceTypeMeta> = {
  jira_issue: {
    label: 'Jira Issue',
    icon: '🎫',
    color: '#ef4444',
    description: 'Import Jira issues (O11Y, TCOC, TCID, ST) for analysis',
  },
  alertstore_alert: {
    label: 'Alertstore Alert',
    icon: '🚨',
    color: '#f97316',
    description: 'Import an alertstore alert (ALT-*) for analysis',
  },
  git_repo: {
    label: 'Git Repo',
    icon: '📦',
    color: '#8b5cf6',
    description: 'Currently Not Supported',
  },
  cluster_meta: {
    label: 'Cluster Meta',
    icon: '🏗️',
    color: '#10b981',
    description: 'Cluster metadata and topology',
  },
  user_meta: {
    label: 'User Meta',
    icon: '👤',
    color: '#6b7280',
    description: 'User context and preferences',
  },
  skill: {
    label: 'Skill',
    icon: '🧠',
    color: '#f59e0b',
    description: 'Agent skill loaded on demand',
  },
};

export const DEFAULT_SOURCE_TYPE_META: SourceTypeMeta = {
  label: 'Unknown',
  icon: '❓',
  color: '#6b7280',
  description: 'Unknown source type',
};

export function getSourceTypeMeta(type: string | undefined): SourceTypeMeta {
  if (!type) return DEFAULT_SOURCE_TYPE_META;
  return (SOURCE_TYPE_META as Record<string, SourceTypeMeta>)[type] ?? {
    ...DEFAULT_SOURCE_TYPE_META,
    label: type,
  };
}

const USER_SOURCE_TYPES = new Set<string>([
  'jira_issue',
  'alertstore_alert',
  'git_repo',
  'cluster_meta',
  'skill',
]);

// ---------------------------------------------------------------------------
// Workspace context
// ---------------------------------------------------------------------------

interface WorkspaceContextType {
  // Workspace identity
  workspaceId: string | undefined;

  // Sources
  sources: Source[];
  addSource: (source: Omit<Source, 'id' | 'createdAt'>) => Promise<Source>;
  updateSource: (id: string, updates: Partial<Omit<Source, 'id' | 'createdAt'>>) => void;
  removeSource: (id: string) => Promise<void>;
  refreshSources: () => Promise<void>;
  selectedSourceId: string | null;
  setSelectedSourceId: (id: string | null) => void;

  // Workspace name change callback (set by parent page to react to auto-rename)
  onWorkspaceNameChange: ((name: string) => void) | null;

  // Skills refresh trigger
  skillsRefreshTrigger: number;
  triggerSkillsRefresh: () => void;

  // Files refresh trigger (event-driven, replaces polling)
  filesRefreshTrigger: number;
  triggerFilesRefresh: () => void;

  // Artifacts
  artifacts: Artifact[];
  addArtifact: (artifact: Omit<Artifact, 'id' | 'createdAt'>) => Artifact;
  updateArtifact: (id: string, updates: Partial<Omit<Artifact, 'id' | 'createdAt'>>) => void;
  removeArtifact: (id: string) => void;
  selectedArtifactId: string | null;
  setSelectedArtifactId: (id: string | null) => void;

  // View state
  studioView: 'list' | 'detail';
  setStudioView: (view: 'list' | 'detail') => void;

  // File viewer
  openFilePath: string | null;
  openFileSizeBytes: number | null;
  setOpenFilePath: (path: string | null, sizeBytes?: number) => void;
}

const WorkspaceContext = createContext<WorkspaceContextType | null>(null);

export function useWorkspace() {
  const ctx = useContext(WorkspaceContext);
  if (!ctx) throw new Error('useWorkspace must be used within WorkspaceProvider');
  return ctx;
}

// ---------------------------------------------------------------------------
// Default sources
// ---------------------------------------------------------------------------

/**
 * System sources injected into every new workspace.
 *
 * - User Meta  : read-only, display-only (context awareness)
 * - Skills     : read-only, display-only capabilities loaded by the agent
 *
 * NOTE: Jira Issue and Cluster Meta sources are NOT included here.
 * - Jira Issue sources are added by the user from the Add Source dialog.
 * - Cluster Meta is generated dynamically once a Jira issue is selected.
 */
function createDefaultSources(): Source[] {
  return [
    // Nutshell skills — loaded on-demand by the OpenCode agent via the skill tool
    {
      id: 'skill-devops',
      type: 'skill',
      name: 'devops',
      description: 'Query tenant/org information from DevOps Portal via REST API',
      displayOnly: true,
      readonly: true,
      meta: { skillName: 'devops' },
      createdAt: new Date(),
    },
    {
      id: 'skill-tidb-perf-diagnosis',
      type: 'skill',
      name: 'tidb-perf-diagnosis',
      description: 'TiDB Cloud performance diagnosis: latency spikes, OOM, hotspots, slow queries, lock contention',
      displayOnly: true,
      readonly: true,
      meta: { skillName: 'tidb-perf-diagnosis' },
      createdAt: new Date(),
    },
    {
      id: 'skill-tikv-fast-tune',
      type: 'skill',
      name: 'tikv-fast-tune',
      description: 'TiKV performance diagnosis using Fast Tune decision tree for write/read latency and jitter',
      displayOnly: true,
      readonly: true,
      meta: { skillName: 'tikv-fast-tune' },
      createdAt: new Date(),
    },
    {
      id: 'skill-o11y-data',
      type: 'skill',
      name: 'o11y-data',
      description: 'Access TiDB Cloud O11Y data: cluster metrics, logs, slowlogs, statements via API and S3',
      displayOnly: true,
      readonly: true,
      meta: { skillName: 'o11y-data' },
      createdAt: new Date(),
    },
    {
      id: 'skill-o11y-metrics-api',
      type: 'skill',
      name: 'o11y-metrics-api',
      description: 'Query TiDB Cloud O11Y metrics via Prometheus-compatible API',
      displayOnly: true,
      readonly: true,
      meta: { skillName: 'o11y-metrics-api' },
      createdAt: new Date(),
    },
    {
      id: 'skill-o11y-alert-rule-lookup',
      type: 'skill',
      name: 'o11y-alert-rule-lookup',
      description: 'Locate matching runbooks/rules alert definitions by O11Y alert name for downstream analysis',
      displayOnly: true,
      readonly: true,
      meta: { skillName: 'o11y-alert-rule-lookup' },
      createdAt: new Date(),
    },
    {
      id: 'skill-clinic-cli',
      type: 'skill',
      name: 'clinic-cli',
      description: 'Default Clinic CLI skill for cluster metadata, metrics, logs, SQL evidence, and diagnostics',
      displayOnly: true,
      readonly: true,
      meta: { skillName: 'clinic-cli' },
      createdAt: new Date(),
    },
    {
      id: 'skill-alertstore-cli',
      type: 'skill',
      name: 'alertstore-cli',
      description: 'Read-only alertstore access for ALT-* operational alerts (detail, timeline, events, list by cluster/tenant)',
      displayOnly: true,
      readonly: true,
      meta: { skillName: 'alertstore-cli' },
      createdAt: new Date(),
    },
    {
      id: 'skill-github-issue-search',
      type: 'skill',
      name: 'github-issue-search',
      description: 'Search GitHub issues and pull requests via the REST Search API (repo, labels, state, full-text)',
      displayOnly: true,
      readonly: true,
      meta: { skillName: 'github-issue-search' },
      createdAt: new Date(),
    },
    {
      id: 'skill-autoscaler-dashboard',
      type: 'skill',
      name: 'autoscaler-dashboard',
      description: 'Evaluate HPA autoscaling quality and generate scaling report dashboards',
      displayOnly: true,
      readonly: true,
      meta: { skillName: 'autoscaler-dashboard' },
      createdAt: new Date(),
    },
    {
      id: 'skill-tidbcloud-api',
      type: 'skill',
      name: 'tidbcloud-api',
      description: 'Manage TiDB Cloud TiDBX clusters: create, delete, list, run SQL, manage backups',
      displayOnly: true,
      readonly: true,
      meta: { skillName: 'tidbcloud-api' },
      createdAt: new Date(),
    },
    {
      id: 'skill-o11y-auth',
      type: 'skill',
      name: 'o11y-auth',
      description: 'Authenticate to TiDB Cloud O11Y platform via GitHub PAT for tokens and AWS credentials',
      displayOnly: true,
      readonly: true,
      meta: { skillName: 'o11y-auth' },
      createdAt: new Date(),
    },
    {
      id: 'skill-o11y-data-export',
      type: 'skill',
      name: 'o11y-data-export',
      description: 'Create and manage O11Y monitoring pipelines: slow queries, raw logs, SQL statements to S3',
      displayOnly: true,
      readonly: true,
      meta: { skillName: 'o11y-data-export' },
      createdAt: new Date(),
    },
    {
      id: 'skill-grafana-panel-finder',
      type: 'skill',
      name: 'grafana-panel-finder',
      description: 'Find Grafana dashboard panels, extract PromQL queries, and generate URLs for TiDB cluster metrics',
      displayOnly: true,
      readonly: true,
      meta: { skillName: 'grafana-panel-finder' },
      createdAt: new Date(),
    },
    {
      id: 'skill-time-conversion',
      type: 'skill',
      name: 'time-conversion',
      description: 'Convert between datetime strings and Unix timestamps (seconds/milliseconds/nanoseconds) with timezone support',
      displayOnly: true,
      readonly: true,
      meta: { skillName: 'time-conversion' },
      createdAt: new Date(),
    },
  ];
}

function createDefaultArtifacts(): Artifact[] {
  // New workspaces start with no pre-built artifacts.
  // Artifacts are generated once the user adds a Jira Issue source and runs analysis.
  return [];
}

// ---------------------------------------------------------------------------
// Example workspace defaults
// ---------------------------------------------------------------------------

/**
 * Sources pre-configured in the Example workspace:
 * - A Jira issue source (O11Y-1956041 slow_query_alert)
 * - A cluster meta source (read-only, from cluster_meta.json)
 * Plus the normal system sources.
 */
function createExampleSources(): Source[] {
  return [
    {
      id: 'demo-alert-1',
      type: 'jira_issue',
      name: 'O11Y-1956041',
      description: 'Slow query alert - TiDB cluster performance issue',
      issueId: 'O11Y-1956041',
      issueDir: 'issues/O11Y-1956041',
      createdAt: new Date(),
    },
    ...createDefaultSources(),
  ];
}

/**
 * Artifacts pre-populated in the Example workspace:
 * - default-report        : Guardian Insight Report (rendered from insight.json)
 * - default-anomaly-report: Anomaly Detection Report (rendered from diagnostic_package.json)
 */
function createExampleArtifacts(): Artifact[] {
  return [
    {
      id: 'default-report',
      type: 'report',
      title: 'Guardian Insight Report',
      description: 'AI-powered analysis of issue O11Y-1956041',
      sourceId: 'demo-alert-1',
      issueId: 'O11Y-1956041',
      issueDir: 'issues/O11Y-1956041',
      createdAt: new Date(),
    },
    {
      id: 'default-anomaly-report',
      type: 'report',
      title: 'Anomaly Detection Report',
      description: 'Outlier detection across cluster metrics',
      sourceId: 'demo-alert-1',
      issueId: 'O11Y-1956041',
      issueDir: 'issues/O11Y-1956041',
      data: { reportType: 'anomaly' },
      createdAt: new Date(),
    },
  ];
}

// ---------------------------------------------------------------------------
// Provider component
// ---------------------------------------------------------------------------

export function WorkspaceProvider({
  children,
  workspaceId,
  canEdit = true,
  onWorkspaceNameChange,
}: {
  children: ReactNode;
  workspaceId?: string;
  canEdit?: boolean;
  onWorkspaceNameChange?: (name: string) => void;
}) {
  const [sources, setSources] = useState<Source[]>([]);
  const [artifacts, setArtifacts] = useState<Artifact[]>([]);
  const [selectedSourceId, setSelectedSourceId] = useState<string | null>(null);
  const [selectedArtifactId, setSelectedArtifactId] = useState<string | null>(null);
  const [studioView, setStudioView] = useState<'list' | 'detail'>('list');
  const [openFilePath, _setOpenFilePath] = useState<string | null>(null);
  const [openFileSizeBytes, setOpenFileSizeBytes] = useState<number | null>(null);
  const setOpenFilePath = useCallback((path: string | null, sizeBytes?: number) => {
    _setOpenFilePath(path);
    setOpenFileSizeBytes(sizeBytes ?? null);
  }, []);
  const [initialized, setInitialized] = useState(false);
  const [skillsRefreshTrigger, setSkillsRefreshTrigger] = useState(0);

  // Load workspace data from backend API
  useEffect(() => {
    if (!workspaceId) {
      // No workspace ID, use defaults
      setSources(createDefaultSources());
      setArtifacts(createDefaultArtifacts());
      return;
    }

    let active = true;

    (async () => {
      try {
        const [rawSources, rawArtifacts] = await Promise.all([
          fetchSources(workspaceId),
          fetchArtifacts(workspaceId),
        ]);
        if (!active) return;

        // Fetch workspace meta once if either table is empty (needed to check is_example).
        // Wrapped in its own try/catch so a network failure here never propagates to the
        // outer catch, which would fire setInitialized before setSources/setArtifacts run.
        let meta: any = null;
        if (rawSources.length === 0 || rawArtifacts.length === 0) {
          try {
            const r = await fetchWithAuth(`${API}/api/workspaces/${encodeURIComponent(workspaceId)}`);
            if (!active) return;
            meta = r.ok ? await r.json() : null;
            if (!active) return;
          } catch {
            // meta stays null — fall back to default sources/artifacts below
          }
        }

        // Set sources
        if (rawSources.length > 0) {
          const parsed: Source[] = rawSources.map((s: any) => ({ ...s, createdAt: new Date(s.createdAt) }));
          const systemSources = createDefaultSources();
          const systemIds = new Set(systemSources.map(s => s.id));
          const userSources = parsed.filter(s => !systemIds.has(s.id) && USER_SOURCE_TYPES.has((s as any).type));
          setSources([...userSources, ...systemSources]);
        } else {
          setSources(meta?.is_example ? createExampleSources() : createDefaultSources());
        }

        // Set artifacts
        if (rawArtifacts.length > 0) {
          setArtifacts(rawArtifacts.map((a: any) => ({ ...a, createdAt: new Date(a.createdAt) })));
        } else {
          setArtifacts(meta?.is_example ? createExampleArtifacts() : createDefaultArtifacts());
        }

        // Mark initialized AFTER data is set so save effects never fire with empty arrays
        setInitialized(true);
      } catch {
        if (active) setInitialized(true);
      }
    })();

    return () => { active = false; };
  }, [workspaceId]);

  // Persist sources to backend whenever they change (skip until initialized)
  useEffect(() => {
    if (!workspaceId || !initialized || !canEdit) return;
    saveSources(workspaceId, sources);
  }, [sources, workspaceId, initialized, canEdit]);

  // Persist artifacts to backend whenever they change
  useEffect(() => {
    if (!workspaceId || !initialized || !canEdit) return;
    saveArtifacts(workspaceId, artifacts);
  }, [artifacts, workspaceId, initialized, canEdit]);

  const addSource = useCallback(async (source: Omit<Source, 'id' | 'createdAt'>): Promise<Source> => {
    if (workspaceId) {
      try {
        const res = await fetchWithAuth(`${API}/api/workspace/${encodeURIComponent(workspaceId)}/sources`, {
          method: 'POST',
          headers: { 'Content-Type': 'application/json' },
          body: JSON.stringify(source),
        });
        const data = await res.json();
        const newSource: Source = { ...data, createdAt: new Date(data.createdAt) };
        setSources(prev => [...prev, newSource]);
        // If backend auto-renamed the workspace, notify parent
        if (data.workspace_name && onWorkspaceNameChange) {
          onWorkspaceNameChange(data.workspace_name);
        }
        return newSource;
      } catch (e) { console.warn('addSource failed:', e); }
    }
    // Fallback for no-workspace mode
    const newSource: Source = { ...source, id: `src-${Date.now()}`, createdAt: new Date() };
    setSources(prev => [...prev, newSource]);
    return newSource;
  }, [workspaceId, onWorkspaceNameChange]);

  const removeSource = useCallback(async (id: string): Promise<void> => {
    if (workspaceId) {
      try {
        await fetchWithAuth(`${API}/api/workspace/${encodeURIComponent(workspaceId)}/sources/${encodeURIComponent(id)}`, {
          method: 'DELETE',
        });
      } catch (e) { console.warn('removeSource failed:', e); }
    }
    setSources(prev => prev.filter(s => s.id !== id || s.readonly));
  }, [workspaceId]);

  const updateSource = useCallback((id: string, updates: Partial<Omit<Source, 'id' | 'createdAt'>>) => {
    setSources(prev => prev.map(s => s.id === id ? { ...s, ...updates } : s));
  }, []);

  const addArtifact = useCallback((artifact: Omit<Artifact, 'id' | 'createdAt'>): Artifact => {
    const newArtifact: Artifact = { ...artifact, id: `art-${Date.now()}`, createdAt: new Date() };
    setArtifacts(prev => [...prev, newArtifact]);
    return newArtifact;
  }, []);

  const updateArtifact = useCallback((id: string, updates: Partial<Omit<Artifact, 'id' | 'createdAt'>>) => {
    setArtifacts(prev => prev.map(a => a.id === id ? { ...a, ...updates } : a));
  }, []);

  const removeArtifact = useCallback((id: string) => {
    setArtifacts(prev => prev.filter(a => a.id !== id));
  }, []);

  const refreshSources = useCallback(async () => {
    if (!workspaceId) return;

    const raw = await fetchSources(workspaceId);
    if (raw.length > 0) {
      const parsed: Source[] = raw.map((s: any) => ({ ...s, createdAt: new Date(s.createdAt) }));
      const systemSources = createDefaultSources();
      const systemIds = new Set(systemSources.map(s => s.id));
      const userSources = parsed.filter(s => !systemIds.has(s.id) && USER_SOURCE_TYPES.has((s as any).type));
      setSources([...userSources, ...systemSources]);
    }
  }, [workspaceId]);

  const triggerSkillsRefresh = useCallback(() => {
    setSkillsRefreshTrigger(prev => prev + 1);
  }, []);

  const [filesRefreshTrigger, setFilesRefreshTrigger] = useState(0);
  const triggerFilesRefresh = useCallback(() => {
    setFilesRefreshTrigger(prev => prev + 1);
  }, []);

  return (
    <WorkspaceContext.Provider
      value={{
        workspaceId,
        sources,
        addSource,
        updateSource,
        removeSource,
        refreshSources,
        selectedSourceId,
        setSelectedSourceId,
        onWorkspaceNameChange: onWorkspaceNameChange ?? null,
        skillsRefreshTrigger,
        triggerSkillsRefresh,
        filesRefreshTrigger,
        triggerFilesRefresh,
        artifacts,
        addArtifact,
        updateArtifact,
        removeArtifact,
        selectedArtifactId,
        setSelectedArtifactId,
        studioView,
        setStudioView,
        openFilePath,
        openFileSizeBytes,
        setOpenFilePath,
      }}
    >
      {children}
    </WorkspaceContext.Provider>
  );
}
