
import { MaterialDTO, MaterialType, UserDTO, UserRole, CommentDTO, SystemConfig, PaginationResult } from '../types';

// --- ENTERPRISE DATA LAYER ---
// This service acts as the Facade Pattern, shielding the UI from the complexity 
// of switching between a mock environment (for dev/demo) and the real backend.

const STORAGE_KEY_MODE = 'NEXUS_DATA_MODE';

const getMode = (): 'MOCK' | 'REAL' => {
  // Default to MOCK for the browser demo environment
  return (localStorage.getItem(STORAGE_KEY_MODE) as 'MOCK' | 'REAL') || 'MOCK';
};

export const setApiMode = (mode: 'MOCK' | 'REAL') => {
  localStorage.setItem(STORAGE_KEY_MODE, mode);
  // Reload to apply changes cleanly across the app
  window.location.reload();
};

// --- MOCK DATABASE STATE ---
const CURRENT_USER: UserDTO = {
  id: 'u_001',
  username: 'Neo_Architect',
  avatarUrl: 'https://picsum.photos/seed/neo/200/200',
  role: UserRole.CREATOR,
  status: 'ACTIVE',
  createdAt: new Date(2023, 10, 15).toISOString(),
  lastLogin: new Date().toISOString()
};

const ALL_USERS: UserDTO[] = [
  CURRENT_USER,
  {
    id: 'u_002',
    username: 'Visual_Drift',
    avatarUrl: 'https://picsum.photos/seed/drift/200/200',
    role: UserRole.CREATOR,
    status: 'ACTIVE',
    createdAt: new Date(2023, 11, 1).toISOString(),
    lastLogin: new Date().toISOString()
  },
  {
    id: 'u_003',
    username: 'Poly_Master',
    avatarUrl: 'https://picsum.photos/seed/poly/200/200',
    role: UserRole.USER,
    status: 'FLAGGED',
    createdAt: new Date(2024, 0, 10).toISOString(),
    lastLogin: new Date().toISOString()
  },
  {
    id: 'u_004',
    username: 'Script_Kiddie',
    avatarUrl: 'https://picsum.photos/seed/kiddie/200/200',
    role: UserRole.USER,
    status: 'BANNED',
    createdAt: new Date(2024, 1, 20).toISOString(),
    lastLogin: new Date(2024, 2, 1).toISOString()
  }
];

const MOCK_MATERIALS: MaterialDTO[] = [
  {
    id: 'm_001',
    title: 'Cyberpunk HUD Interface React',
    description: 'A complete React component set for building sci-fi interfaces. Includes glitch effects and holographic transitions.',
    type: MaterialType.CODE,
    codeSnippet: `const CyberButton = ({ children }) => (
  <button className="border border-neon-500 text-neon-500 hover:bg-neon-500 hover:text-black transition-all duration-300 uppercase tracking-widest px-6 py-2">
    {children}
  </button>
);`,
    language: 'tsx',
    author: CURRENT_USER,
    stats: { views: 1204, downloads: 450, favorites: 89 },
    tags: ['react', 'ui', 'cyberpunk', 'animation'],
    createdAt: new Date(Date.now() - 86400000 * 2).toISOString(),
    comments: []
  },
  {
    id: 'm_002',
    title: 'Neon City Ambience Loop',
    description: '4K loop of a futuristic city rain scene. Perfect for background assets.',
    type: MaterialType.VIDEO,
    contentUrl: 'https://media.w3.org/2010/05/sintel/trailer.mp4', // Placeholder video
    author: ALL_USERS[1],
    stats: { views: 5320, downloads: 1200, favorites: 432 },
    tags: ['video', 'loop', 'background', '4k'],
    createdAt: new Date(Date.now() - 86400000 * 5).toISOString(),
    comments: [
      {
        id: 'c_1',
        content: 'The lighting on this is insane!',
        author: CURRENT_USER,
        createdAt: new Date().toISOString()
      }
    ]
  },
  {
    id: 'm_003',
    title: 'Industrial 3D Assets Pack',
    description: 'Low-poly mechanical parts for game dev. GLB format + Textures.',
    type: MaterialType.ASSET_ZIP,
    contentUrl: '#',
    author: ALL_USERS[2],
    stats: { views: 800, downloads: 120, favorites: 45 },
    tags: ['3d', 'blender', 'game-assets'],
    createdAt: new Date(Date.now() - 86400000 * 1).toISOString(),
    comments: []
  }
];

// Generate additional code materials for testing (total 40)
if (MOCK_MATERIALS.length < 40) {
  const base = MOCK_MATERIALS.length;
  const types = [MaterialType.CODE, MaterialType.ASSET_ZIP, MaterialType.VIDEO];
  for (let i = base; i < 40; i++) {
    const id = `m_${(i + 1).toString().padStart(3, '0')}`;
    const author = ALL_USERS[i % ALL_USERS.length];
    const t = types[i % types.length];
    const createdAt = new Date(Date.now() - 86400000 * (i % 10)).toISOString();
    const stats = { views: Math.floor(Math.random() * 1000), downloads: Math.floor(Math.random() * 200), favorites: Math.floor(Math.random() * 50) };
    const common = {
      id,
      title: t === MaterialType.CODE ? `Test Code Snippet #${i + 1}` : t === MaterialType.ASSET_ZIP ? `Assets Pack #${i + 1}` : `Demo Video #${i + 1}`,
      description: 'Auto-generated demo material.',
      type: t,
      author,
      stats,
      tags: t === MaterialType.CODE ? ['test', 'code'] : t === MaterialType.ASSET_ZIP ? ['assets'] : ['video'],
      createdAt,
      comments: [] as any[]
    };
    if (t === MaterialType.CODE) {
      MOCK_MATERIALS.push({
        ...common,
        codeSnippet: `function test${i}(){return ${i};}`,
        language: 'ts'
      });
    } else if (t === MaterialType.ASSET_ZIP) {
      MOCK_MATERIALS.push({
        ...common,
        contentUrl: '#'
      });
    } else {
      MOCK_MATERIALS.push({
        ...common,
        contentUrl: 'https://media.w3.org/2010/05/sintel/trailer.mp4'
      });
    }
  }
}

let SYSTEM_CONFIG: SystemConfig = {
  dbHost: '127.0.0.1',
  dbPort: '3306',
  dbUser: 'root',
  dbPass: 'admin123',
  dbName: 'nexus_db',
  maintenanceMode: false,
  maxUploadMB: 3
};

// Helper to call backend API in REAL mode
async function apiCall<T>(endpoint: string, options?: RequestInit): Promise<T> {
  try {
    const res = await fetch(`/api/v1${endpoint}`, {
      ...options,
      headers: {
        'Content-Type': 'application/json',
        ...options?.headers
      }
    });

    const json = await res.json();

    if (!res.ok) {
      // Return error as part of response structure
      throw new Error(json.error || res.statusText || 'Request failed');
    }

    return json.data; // Assuming ApiResponse wrapper
  } catch (e) {
    console.error("API Call Failed:", e);
    throw e; // Re-throw to be caught by caller
  }
}

// --- SERVICE METHODS ---

export const DataService = {

  getMode,
  setApiMode,

  // --- CLIENT SIDE ---
  getCurrentUser: async (): Promise<UserDTO | null> => {
    if (getMode() === 'REAL') {
      try {
        return await apiCall<UserDTO>('/auth/me');
      } catch (error) {
        // User not logged in or token expired, return null
        console.log('User not authenticated');
        return null;
      }
    }
    return new Promise(resolve => setTimeout(() => resolve(CURRENT_USER), 500));
  },

  updateUserProfile: async (userId: string, data: Partial<UserDTO>): Promise<UserDTO> => {
    if (getMode() === 'REAL') return apiCall<UserDTO>('/users/me', { method: 'PATCH', body: JSON.stringify(data) });

    return new Promise(resolve => {
      if (CURRENT_USER.id === userId) {
        if (data.username) CURRENT_USER.username = data.username;
        if (data.avatarUrl) CURRENT_USER.avatarUrl = data.avatarUrl;
      }
      setTimeout(() => resolve({ ...CURRENT_USER }), 800);
    });
  },

  getMaterials: async (page = 1, limit = 20, type?: MaterialType | 'ALL', search?: string): Promise<PaginationResult<MaterialDTO>> => {
    const params = new URLSearchParams({ page: String(page), limit: String(limit) });
    if (type && type !== 'ALL') params.append('type', String(type));
    if (search && search.trim()) params.append('q', search.trim());
    if (getMode() === 'REAL') return apiCall<PaginationResult<MaterialDTO>>(`/materials?${params.toString()}`);

    let source = type && type !== 'ALL' ? MOCK_MATERIALS.filter(m => m.type === type) : MOCK_MATERIALS;
    if (search && search.trim()) {
      const q = search.trim().toLowerCase();
      source = source.filter(m => {
        const inTitle = m.title.toLowerCase().includes(q);
        const inDesc = m.description.toLowerCase().includes(q);
        const inTags = m.tags.some(t => t.toLowerCase().includes(q));
        return inTitle || inDesc || inTags;
      });
    }
    const start = (page - 1) * limit;
    const end = start + limit;
    const items = source.slice(start, end);
    const total = source.length;
    const hasNext = page * limit < total;
    return new Promise(resolve => setTimeout(() => resolve({ items, total, page, limit, hasNext }), 400));
  },

  getUserMaterials: async (page = 1, limit = 20): Promise<PaginationResult<MaterialDTO>> => {
    if (getMode() === 'REAL') return apiCall<PaginationResult<MaterialDTO>>(`/users/me/materials?page=${page}&limit=${limit}`);
    const userItems = MOCK_MATERIALS.filter(m => m.author.id === CURRENT_USER.id);
    const start = (page - 1) * limit;
    const end = start + limit;
    const items = userItems.slice(start, end);
    const total = userItems.length;
    const hasNext = page * limit < total;
    return new Promise(resolve => setTimeout(() => resolve({ items, total, page, limit, hasNext }), 300));
  },

  getUserFavorites: async (page = 1, limit = 20): Promise<PaginationResult<MaterialDTO>> => {
    if (getMode() === 'REAL') return apiCall<PaginationResult<MaterialDTO>>(`/users/me/favorites?page=${page}&limit=${limit}`);
    const favItems = MOCK_MATERIALS.filter(m => m.stats.favorites > 0);
    const start = (page - 1) * limit;
    const end = start + limit;
    const items = favItems.slice(start, end);
    const total = favItems.length;
    const hasNext = page * limit < total;
    return new Promise(resolve => setTimeout(() => resolve({ items, total, page, limit, hasNext }), 300));
  },

  getMaterialById: async (id: string): Promise<MaterialDTO | undefined> => {
    if (getMode() === 'REAL') return apiCall<MaterialDTO>(`/materials/${id}`);

    return new Promise(resolve => {
      const item = MOCK_MATERIALS.find(m => m.id === id);
      setTimeout(() => resolve(item), 400);
    });
  },

  createMaterial: async (data: Partial<MaterialDTO>): Promise<MaterialDTO> => {
    if (getMode() === 'REAL') return apiCall<MaterialDTO>('/materials', { method: 'POST', body: JSON.stringify(data) });

    return new Promise(resolve => {
      const newMaterial: MaterialDTO = {
        id: `m_${Date.now()}`,
        title: data.title || 'Untitled',
        description: data.description || '',
        type: data.type || MaterialType.CODE,
        codeSnippet: data.codeSnippet,
        contentUrl: data.contentUrl,
        language: data.language || 'text',
        author: CURRENT_USER,
        stats: { views: 0, downloads: 0, favorites: 0 },
        tags: data.tags || [],
        createdAt: new Date().toISOString(),
        comments: []
      };
      MOCK_MATERIALS.unshift(newMaterial);
      setTimeout(() => resolve(newMaterial), 1000);
    });
  },

  deleteMaterial: async (id: string): Promise<boolean> => {
    if (getMode() === 'REAL') {
      await apiCall(`/materials/${id}`, { method: 'DELETE' });
      return true;
    }

    const idx = MOCK_MATERIALS.findIndex(m => m.id === id);
    if (idx > -1) {
      MOCK_MATERIALS.splice(idx, 1);
      return true;
    }
    return false;
  },

  addComment: async (materialId: string, content: string): Promise<CommentDTO> => {
    if (getMode() === 'REAL') return apiCall<CommentDTO>(`/materials/${materialId}/comments`, { method: 'POST', body: JSON.stringify({ content }) });

    const material = MOCK_MATERIALS.find(m => m.id === materialId);
    if (!material) throw new Error('Material not found');

    const newComment: CommentDTO = {
      id: `c_${Date.now()}`,
      content,
      author: CURRENT_USER,
      createdAt: new Date().toISOString()
    };

    material.comments.push(newComment);
    return newComment;
  },

  toggleFavorite: async (materialId: string): Promise<number> => {
    if (getMode() === 'REAL') {
      const res = await apiCall<{ favorites: number }>(`/materials/${materialId}/favorite`, { method: 'POST' });
      return res.favorites;
    }

    const material = MOCK_MATERIALS.find(m => m.id === materialId);
    if (material) {
      // Toggle logic simulation
      material.stats.favorites += 1;
      return material.stats.favorites;
    }
    return 0;
  },

  uploadZip: async (file: File, meta?: { title?: string; description?: string; tags?: string[] }) => {
    const fd = new FormData();
    fd.append('file', file);
    if (meta?.title) fd.append('title', meta.title);
    if (meta?.description) fd.append('description', meta.description);
    if (meta?.tags) fd.append('tags', meta.tags.join(','));
    const res = await fetch('/api/v1/materials/upload-zip', { method: 'POST', body: fd });
    const ct = res.headers.get('content-type') || '';
    if (!ct.includes('application/json')) {
      const text = await res.text();
      throw new Error(text || `Upload failed (${res.status})`);
    }
    const json = await res.json();
    if (!res.ok || !json.success) throw new Error(json.error || `Upload failed (${res.status})`);
    return json.data as MaterialDTO;
  },

  uploadVideo: async (file: File, meta?: { title?: string; description?: string; tags?: string[] }) => {
    const fd = new FormData();
    fd.append('file', file);
    if (meta?.title) fd.append('title', meta.title);
    if (meta?.description) fd.append('description', meta.description);
    if (meta?.tags) fd.append('tags', meta.tags.join(','));
    const res = await fetch('/api/v1/materials/upload-video', { method: 'POST', body: fd });
    const ct = res.headers.get('content-type') || '';
    if (!ct.includes('application/json')) {
      const text = await res.text();
      throw new Error(text || `Upload failed (${res.status})`);
    }
    const json = await res.json();
    if (!res.ok || !json.success) throw new Error(json.error || `Upload failed (${res.status})`);
    return json.data as MaterialDTO;
  },

  // --- ADMIN SIDE ---
  verifyAdmin: async (u: string, p: string): Promise<boolean> => {
    // Admin login is usually separate or special
    return new Promise(resolve => {
      setTimeout(() => {
        resolve(u === 'admin' && p === 'wx1998WX');
      }, 1000);
    });
  },

  getAllUsers: async (): Promise<UserDTO[]> => {
    if (getMode() === 'REAL') return apiCall<UserDTO[]>('/admin/users');
    return new Promise(resolve => setTimeout(() => resolve(ALL_USERS), 600));
  },

  updateUserRole: async (userId: string, role: UserRole): Promise<UserDTO> => {
    if (getMode() === 'REAL') return apiCall<UserDTO>(`/admin/users/${userId}/role`, { method: 'POST', body: JSON.stringify({ role }) });
    const u = ALL_USERS.find(user => user.id === userId);
    if (!u) throw new Error('User not found');
    u.role = role;
    return new Promise(resolve => setTimeout(() => resolve({ ...u }), 400));
  },

  getSystemConfig: async (): Promise<SystemConfig> => {
    if (getMode() === 'REAL') return apiCall<SystemConfig>('/admin/config');
    return SYSTEM_CONFIG;
  },

  updateSystemConfig: async (config: SystemConfig): Promise<void> => {
    if (getMode() === 'REAL') {
      await apiCall('/admin/config', {
        method: 'PUT',
        body: JSON.stringify({
          maxUploadMB: config.maxUploadMB,
          uploadDir: 'data',
          dbHost: config.dbHost,
          dbPort: config.dbPort,
          dbUser: config.dbUser,
          dbPass: config.dbPass,
          dbName: config.dbName,
        })
      });
      return;
    }
    SYSTEM_CONFIG = config;
    return new Promise(resolve => setTimeout(resolve, 1500));
  },

  toggleUserStatus: async (userId: string): Promise<UserDTO | undefined> => {
    if (getMode() === 'REAL') return apiCall<UserDTO>(`/admin/users/${userId}/toggle-status`, { method: 'POST' });

    const u = ALL_USERS.find(user => user.id === userId);
    if (u) {
      u.status = u.status === 'ACTIVE' ? 'BANNED' : 'ACTIVE';
      return u;
    }
  }
};
