import { 
  type Video, type InsertVideo, type UpdateVideo,
  type User, type InsertUser,
  type VideoUpload, type InsertVideoUpload,
  type Category, type InsertCategory,
  type Tag, type InsertTag,
  videos, users, videoUploads, categories, tags, videoTags
} from "@shared/schema";
import { randomUUID } from "crypto";
import { BunnyService } from "./bunny";
import { videoSyncService } from "./videoSync";
import { db } from "./db";
import { eq, desc, asc, like, or, sql, and } from "drizzle-orm";
import bcrypt from "bcryptjs";

export interface IStorage {
  // Video operations
  getVideos(limit?: number, offset?: number, search?: string): Promise<Video[]>;
  getVideo(id: string): Promise<Video | undefined>;
  createVideo(video: InsertVideo): Promise<Video>;
  updateVideo(id: string, video: UpdateVideo): Promise<Video | undefined>;
  updateVideoViews(id: string): Promise<void>;
  getVideoCount(search?: string): Promise<number>;
  deleteVideo(id: string): Promise<boolean>;

  // User operations
  getUser(id: string): Promise<User | undefined>;
  getUserByEmail(email: string): Promise<User | undefined>;
  getUserByUsername(username: string): Promise<User | undefined>;
  getAllUsers(limit?: number, offset?: number, search?: string): Promise<User[]>;
  getUserCount(search?: string): Promise<number>;
  createUser(user: InsertUser): Promise<User>;
  updateUser(id: string, user: Partial<InsertUser>): Promise<User | undefined>;
  upsertUser(user: any): Promise<User>;
  validateUserPassword(email: string, password: string): Promise<User | null>;

  // Upload operations
  createVideoUpload(upload: InsertVideoUpload): Promise<VideoUpload>;
  getVideoUpload(id: string): Promise<VideoUpload | undefined>;
  updateVideoUpload(id: string, upload: Partial<InsertVideoUpload>): Promise<VideoUpload | undefined>;
  getUserVideoUploads(userId: string): Promise<VideoUpload[]>;

  // Category operations
  getCategories(): Promise<Category[]>;
  createCategory(category: InsertCategory): Promise<Category>;
  updateCategory(id: string, category: Partial<InsertCategory>): Promise<Category | undefined>;
  deleteCategory(id: string): Promise<boolean>;

  // Tag operations
  getTags(): Promise<Tag[]>;
  getPopularTags(limit?: number): Promise<Tag[]>;
  createTag(tag: InsertTag): Promise<Tag>;
  incrementTagUse(name: string): Promise<void>;
}

// Database storage implementation using PostgreSQL
export class DatabaseStorage implements IStorage {
  // Video operations
  async getVideos(limit = 20, offset = 0, search?: string): Promise<Video[]> {
    let query = db.select().from(videos);
    
    if (search) {
      const searchTerm = `%${search}%`;
      query = query.where(
        or(
          like(videos.title, searchTerm),
          like(videos.description, searchTerm)
        )
      ) as any;
    }
    
    const result = await query
      .orderBy(desc(videos.createdAt))
      .limit(limit)
      .offset(offset);
    
    return result;
  }

  async getVideo(id: string): Promise<Video | undefined> {
    const result = await db.select().from(videos).where(eq(videos.id, id));
    return result[0];
  }

  async createVideo(video: InsertVideo): Promise<Video> {
    const result = await db.insert(videos).values({
      ...video,
      updatedAt: new Date()
    }).returning();
    return result[0];
  }

  async updateVideo(id: string, updates: UpdateVideo): Promise<Video | undefined> {
    const result = await db.update(videos)
      .set({ ...updates, updatedAt: new Date() })
      .where(eq(videos.id, id))
      .returning();
    return result[0];
  }

  async updateVideoViews(id: string): Promise<void> {
    await db.update(videos)
      .set({ views: sql`${videos.views} + 1` })
      .where(eq(videos.id, id));
  }

  async getVideoCount(search?: string): Promise<number> {
    let query = db.select({ count: sql<number>`count(*)` }).from(videos);
    
    if (search) {
      const searchTerm = `%${search}%`;
      query = query.where(
        or(
          like(videos.title, searchTerm),
          like(videos.description, searchTerm)
        )
      ) as any;
    }
    
    const result = await query;
    return result[0].count;
  }

  async deleteVideo(id: string): Promise<boolean> {
    const result = await db.delete(videos).where(eq(videos.id, id));
    return (result.rowCount ?? 0) > 0;
  }

  // User operations
  async getUser(id: string): Promise<User | undefined> {
    const result = await db.select().from(users).where(eq(users.id, id));
    return result[0];
  }

  async getUserByEmail(email: string): Promise<User | undefined> {
    const result = await db.select().from(users).where(eq(users.email, email));
    return result[0];
  }

  async getUserByUsername(username: string): Promise<User | undefined> {
    const result = await db.select().from(users).where(eq(users.username, username));
    return result[0];
  }

  async createUser(user: InsertUser): Promise<User> {
    // Hash password before storing
    const hashedPassword = await bcrypt.hash(user.passwordHash, 12);
    
    const { passwordHash: _, ...userWithoutPassword } = user;
    const result = await db.insert(users).values({
      ...userWithoutPassword,
      passwordHash: hashedPassword,
      updatedAt: new Date()
    } as any).returning();
    return result[0];
  }

  async updateUser(id: string, updates: Partial<InsertUser>): Promise<User | undefined> {
    const updateData: any = { ...updates, updatedAt: new Date() };
    
    // Hash password if it's being updated
    if (updates.passwordHash) {
      updateData.passwordHash = await bcrypt.hash(updates.passwordHash, 12);
    }
    
    const result = await db.update(users)
      .set(updateData)
      .where(eq(users.id, id))
      .returning();
    return result[0];
  }

  async upsertUser(userData: any): Promise<User> {
    const [user] = await db
      .insert(users)
      .values({
        id: userData.id,
        email: userData.email,
        firstName: userData.firstName,
        lastName: userData.lastName,
        avatar: userData.profileImageUrl,
        username: userData.email || `user_${userData.id}`,
        passwordHash: '', // No password for OAuth users
        isAdmin: false,
        isSuperAdmin: false,
      })
      .onConflictDoUpdate({
        target: users.id,
        set: {
          email: userData.email,
          firstName: userData.firstName,
          lastName: userData.lastName,
          avatar: userData.profileImageUrl,
          updatedAt: new Date(),
        },
      })
      .returning();
    return user;
  }

  async getAllUsers(limit = 50, offset = 0, search?: string): Promise<User[]> {
    let query = db.select().from(users);
    
    if (search) {
      const searchTerm = `%${search}%`;
      query = query.where(
        or(
          like(users.username, searchTerm),
          like(users.email, searchTerm),
          like(users.firstName, searchTerm),
          like(users.lastName, searchTerm)
        )
      ) as any;
    }
    
    const result = await query
      .orderBy(desc(users.createdAt))
      .limit(limit)
      .offset(offset);
    
    return result;
  }

  async getUserCount(search?: string): Promise<number> {
    let query = db.select({ count: sql<number>`count(*)` }).from(users);
    
    if (search) {
      const searchTerm = `%${search}%`;
      query = query.where(
        or(
          like(users.username, searchTerm),
          like(users.email, searchTerm),
          like(users.firstName, searchTerm),
          like(users.lastName, searchTerm)
        )
      ) as any;
    }
    
    const result = await query;
    return result[0].count;
  }

  async validateUserPassword(email: string, password: string): Promise<User | null> {
    const user = await this.getUserByEmail(email);
    if (!user) return null;
    
    const isValid = await bcrypt.compare(password, user.passwordHash);
    return isValid ? user : null;
  }

  // Upload operations
  async createVideoUpload(upload: InsertVideoUpload): Promise<VideoUpload> {
    const result = await db.insert(videoUploads).values({
      ...upload,
      updatedAt: new Date()
    }).returning();
    return result[0];
  }

  async getVideoUpload(id: string): Promise<VideoUpload | undefined> {
    const result = await db.select().from(videoUploads).where(eq(videoUploads.id, id));
    return result[0];
  }

  async updateVideoUpload(id: string, updates: Partial<InsertVideoUpload>): Promise<VideoUpload | undefined> {
    const result = await db.update(videoUploads)
      .set({ ...updates, updatedAt: new Date() })
      .where(eq(videoUploads.id, id))
      .returning();
    return result[0];
  }

  async getUserVideoUploads(userId: string): Promise<VideoUpload[]> {
    return await db.select().from(videoUploads)
      .where(eq(videoUploads.userId, userId))
      .orderBy(desc(videoUploads.createdAt));
  }

  // Category operations
  async getCategories(): Promise<Category[]> {
    return await db.select().from(categories).orderBy(asc(categories.name));
  }

  async createCategory(category: InsertCategory): Promise<Category> {
    const result = await db.insert(categories).values(category).returning();
    return result[0];
  }

  async updateCategory(id: string, updates: Partial<InsertCategory>): Promise<Category | undefined> {
    const result = await db.update(categories)
      .set(updates)
      .where(eq(categories.id, id))
      .returning();
    return result[0];
  }

  async deleteCategory(id: string): Promise<boolean> {
    const result = await db.delete(categories).where(eq(categories.id, id));
    return (result.rowCount ?? 0) > 0;
  }

  // Tag operations
  async getTags(): Promise<Tag[]> {
    return await db.select().from(tags).orderBy(desc(tags.useCount));
  }

  async getPopularTags(limit = 10): Promise<Tag[]> {
    return await db.select().from(tags)
      .orderBy(desc(tags.useCount))
      .limit(limit);
  }

  async createTag(tag: InsertTag): Promise<Tag> {
    const result = await db.insert(tags).values(tag).returning();
    return result[0];
  }

  async incrementTagUse(name: string): Promise<void> {
    await db.update(tags)
      .set({ useCount: sql`${tags.useCount} + 1` })
      .where(eq(tags.name, name));
  }
}

export class MemStorage implements IStorage {
  private videos: Map<string, Video>;
  private users: Map<string, User>;
  private uploads: Map<string, VideoUpload>;
  private categoriesMap: Map<string, Category>;
  private tagsMap: Map<string, Tag>;

  constructor() {
    this.videos = new Map();
    this.users = new Map();
    this.uploads = new Map();
    this.categoriesMap = new Map();
    this.tagsMap = new Map();
    this.initializeSampleData();
  }

  private async initializeSampleData() {
    // Initialize sample categories
    const sampleCategories: Category[] = [
      { id: randomUUID(), name: "Tutorial", description: "Educational content", color: "#3B82F6", createdAt: new Date() },
      { id: randomUUID(), name: "Business", description: "Business and professional content", color: "#10B981", createdAt: new Date() },
      { id: randomUUID(), name: "Design", description: "UI/UX and design content", color: "#F59E0B", createdAt: new Date() },
      { id: randomUUID(), name: "Analytics", description: "Data and analytics content", color: "#EF4444", createdAt: new Date() },
      { id: randomUUID(), name: "Mobile", description: "Mobile development", color: "#8B5CF6", createdAt: new Date() },
      { id: randomUUID(), name: "DevOps", description: "DevOps and infrastructure", color: "#06B6D4", createdAt: new Date() }
    ];
    
    sampleCategories.forEach(category => {
      this.categoriesMap.set(category.id, category);
    });

    // Initialize sample tags
    const sampleTags: Tag[] = [
      { id: randomUUID(), name: "react", useCount: 25, createdAt: new Date() },
      { id: randomUUID(), name: "typescript", useCount: 20, createdAt: new Date() },
      { id: randomUUID(), name: "javascript", useCount: 30, createdAt: new Date() },
      { id: randomUUID(), name: "frontend", useCount: 18, createdAt: new Date() },
      { id: randomUUID(), name: "backend", useCount: 15, createdAt: new Date() },
      { id: randomUUID(), name: "tutorial", useCount: 40, createdAt: new Date() },
    ];
    
    sampleTags.forEach(tag => {
      this.tagsMap.set(tag.id, tag);
    });

    this.initializeSampleVideos();
  }

  private initializeSampleVideos() {
    const sampleVideos: InsertVideo[] = [
      {
        title: "Advanced Web Development Techniques and Best Practices",
        description: "Learn modern web development techniques including React, TypeScript, and performance optimization strategies.",
        thumbnailUrl: "https://images.unsplash.com/photo-1560472355-536de3962603?ixlib=rb-4.0.3&auto=format&fit=crop&w=800&h=450",
        videoUrl: "https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4",
        duration: 754, // 12:34
        views: 2100,
        category: "Tutorial"
      },
      {
        title: "Team Collaboration Strategies for Remote Work",
        description: "Effective strategies for managing remote teams and improving collaboration in distributed environments.",
        thumbnailUrl: "https://images.unsplash.com/photo-1552664730-d307ca884978?ixlib=rb-4.0.3&auto=format&fit=crop&w=800&h=450",
        videoUrl: "https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ElephantsDream.mp4",
        duration: 495, // 8:15
        views: 856,
        category: "Business"
      },
      {
        title: "Modern UI/UX Design Principles and Workflows",
        description: "Comprehensive guide to modern design principles, user experience optimization, and design system creation.",
        thumbnailUrl: "https://images.unsplash.com/photo-1581291518857-4e27b48ff24e?ixlib=rb-4.0.3&auto=format&fit=crop&w=800&h=450",
        videoUrl: "https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ForBiggerBlazes.mp4",
        duration: 942, // 15:42
        views: 3700,
        category: "Design"
      },
      {
        title: "Data Analytics and Business Intelligence Tutorial",
        description: "Learn how to analyze data effectively using modern tools and create meaningful business insights.",
        thumbnailUrl: "https://images.unsplash.com/photo-1551288049-bebda4e38f71?ixlib=rb-4.0.3&auto=format&fit=crop&w=800&h=450",
        videoUrl: "https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ForBiggerEscapes.mp4",
        duration: 1328, // 22:08
        views: 1400,
        category: "Analytics"
      },
      {
        title: "Mobile App Development: From Concept to Launch",
        description: "Complete guide to mobile app development covering design, development, testing, and deployment strategies.",
        thumbnailUrl: "https://images.unsplash.com/photo-1512941937669-90a1b58e7e9c?ixlib=rb-4.0.3&auto=format&fit=crop&w=800&h=450",
        videoUrl: "https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ForBiggerFun.mp4",
        duration: 1113, // 18:33
        views: 4200,
        category: "Mobile"
      },
      {
        title: "Cloud Infrastructure and DevOps Fundamentals",
        description: "Understanding cloud computing, infrastructure as code, and DevOps practices for modern applications.",
        thumbnailUrl: "https://images.unsplash.com/photo-1558494949-ef010cbdcc31?ixlib=rb-4.0.3&auto=format&fit=crop&w=800&h=450",
        videoUrl: "https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ForBiggerJoyrides.mp4",
        duration: 1517, // 25:17
        views: 987,
        category: "DevOps"
      }
    ];

    sampleVideos.forEach(video => {
      const id = randomUUID();
      const fullVideo: Video = {
        ...video,
        id,
        description: video.description || "",
        category: video.category || "",
        artist: null, // Add artist field
        filename: null, // Original filename
        episodeNumber: null, // Episode number for shows
        episodeTitle: null, // Episode title for shows
        customThumbnailUrl: null,
        faceCenterPosition: null,
        facesDetected: 0,
        faceConfidence: 0,
        videoUrlMp4: null,
        videoUrlIframe: null,
        tags: [],
        isPublic: true,
        views: video.views || 0,
        contentType: video.contentType || 'video',
        genre: video.genre || 'other',
        createdAt: new Date(),
        updatedAt: new Date(),
        uploadStatus: video.uploadStatus || "completed",
        originalFileName: video.originalFileName || null,
        fileSize: video.fileSize || null,
        bitrate: video.bitrate || null,
        resolution: video.resolution || null,
        format: video.format || null,
        encoding: video.encoding || null
      };
      this.videos.set(id, fullVideo);
    });
  }

  async getVideos(limit = 20, offset = 0, search?: string): Promise<Video[]> {
    let videos = Array.from(this.videos.values());

    // Optimized search - only search meaningful queries (2+ chars)
    if (search && search.length >= 2) {
      const searchLower = search.toLowerCase();
      videos = videos.filter(video => {
        // Check title first (most common match)
        if (video.title.toLowerCase().includes(searchLower)) return true;
        // Check description if exists
        return video.description?.toLowerCase().includes(searchLower) || false;
      });
    }

    // Sort by created date (newest first) - more efficient sort
    videos.sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime());

    return videos.slice(offset, offset + limit);
  }

  async getVideo(id: string): Promise<Video | undefined> {
    return this.videos.get(id);
  }

  async createVideo(video: InsertVideo): Promise<Video> {
    const id = randomUUID();
    const fullVideo: Video = {
      ...video,
      id,
      description: video.description || "",
      category: video.category || "",
      artist: video.artist || null, // Add artist field
      filename: video.filename || null,
      episodeNumber: video.episodeNumber || null,
      episodeTitle: video.episodeTitle || null,
      customThumbnailUrl: null,
      faceCenterPosition: null,
      facesDetected: 0,
      faceConfidence: 0,
      videoUrlMp4: null,
      videoUrlIframe: null,
      tags: video.tags || [],
      isPublic: video.isPublic ?? true,
      views: video.views || 0,
      contentType: video.contentType || 'video',
      genre: video.genre || 'other',
      uploadStatus: video.uploadStatus || "completed",
      originalFileName: video.originalFileName || null,
      fileSize: video.fileSize || null,
      bitrate: video.bitrate || null,
      resolution: video.resolution || null,
      format: video.format || null,
      encoding: video.encoding || null,
      createdAt: new Date(),
      updatedAt: new Date()
    };
    this.videos.set(id, fullVideo);
    return fullVideo;
  }

  async updateVideo(id: string, updates: UpdateVideo): Promise<Video | undefined> {
    const video = this.videos.get(id);
    if (!video) return undefined;

    const updatedVideo: Video = {
      ...video,
      ...updates,
      updatedAt: new Date()
    };
    this.videos.set(id, updatedVideo);
    return updatedVideo;
  }

  async updateVideoViews(id: string): Promise<void> {
    const video = this.videos.get(id);
    if (video) {
      video.views += 1;
      this.videos.set(id, video);
    }
  }

  async getVideoCount(search?: string): Promise<number> {
    // More efficient count without loading all data
    let videos = Array.from(this.videos.values());

    if (search && search.length >= 2) {
      const searchLower = search.toLowerCase();
      videos = videos.filter(video => 
        video.title.toLowerCase().includes(searchLower) ||
        video.description?.toLowerCase().includes(searchLower)
      );
    }

    return videos.length;
  }

  async deleteVideo(id: string): Promise<boolean> {
    return this.videos.delete(id);
  }

  // User operations
  async getUser(id: string): Promise<User | undefined> {
    return this.users.get(id);
  }

  async getUserByEmail(email: string): Promise<User | undefined> {
    return Array.from(this.users.values()).find(user => user.email === email);
  }

  async getUserByUsername(username: string): Promise<User | undefined> {
    return Array.from(this.users.values()).find(user => user.username === username);
  }

  async createUser(user: InsertUser): Promise<User> {
    const id = randomUUID();
    const hashedPassword = await bcrypt.hash(user.passwordHash, 12);
    
    const fullUser: User = {
      ...user,
      id,
      passwordHash: hashedPassword,
      firstName: user.firstName || null,
      lastName: user.lastName || null,
      avatar: user.avatar || null,
      isActive: user.isActive ?? true,
      isAdmin: user.isAdmin ?? false,
      isSuperAdmin: user.isSuperAdmin ?? false,
      createdAt: new Date(),
      updatedAt: new Date()
    };
    this.users.set(id, fullUser);
    return fullUser;
  }

  async updateUser(id: string, updates: Partial<InsertUser>): Promise<User | undefined> {
    const user = this.users.get(id);
    if (!user) return undefined;

    const updateData: any = { ...updates, updatedAt: new Date() };
    if (updates.passwordHash) {
      updateData.passwordHash = await bcrypt.hash(updates.passwordHash, 12);
    }

    const updatedUser: User = {
      ...user,
      ...updateData
    };
    this.users.set(id, updatedUser);
    return updatedUser;
  }

  async upsertUser(userData: any): Promise<User> {
    const existingUser = this.users.get(userData.id);
    const user: User = {
      id: userData.id,
      email: userData.email,
      firstName: userData.firstName,
      lastName: userData.lastName,
      avatar: userData.profileImageUrl,
      username: userData.email || `user_${userData.id}`,
      passwordHash: '',
      isActive: existingUser?.isActive ?? true,
      isAdmin: existingUser?.isAdmin || false,
      isSuperAdmin: existingUser?.isSuperAdmin || false,
      createdAt: existingUser?.createdAt || new Date(),
      updatedAt: new Date(),
    };
    this.users.set(userData.id, user);
    return user;
  }

  async getAllUsers(limit = 50, offset = 0, search?: string): Promise<User[]> {
    let users = Array.from(this.users.values());

    if (search && search.length >= 2) {
      const searchLower = search.toLowerCase();
      users = users.filter(user => 
        user.username.toLowerCase().includes(searchLower) ||
        user.email.toLowerCase().includes(searchLower) ||
        user.firstName?.toLowerCase().includes(searchLower) ||
        user.lastName?.toLowerCase().includes(searchLower)
      );
    }

    // Sort by created date (newest first)
    users.sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime());

    return users.slice(offset, offset + limit);
  }

  async getUserCount(search?: string): Promise<number> {
    let users = Array.from(this.users.values());

    if (search && search.length >= 2) {
      const searchLower = search.toLowerCase();
      users = users.filter(user => 
        user.username.toLowerCase().includes(searchLower) ||
        user.email.toLowerCase().includes(searchLower) ||
        user.firstName?.toLowerCase().includes(searchLower) ||
        user.lastName?.toLowerCase().includes(searchLower)
      );
    }

    return users.length;
  }

  async validateUserPassword(email: string, password: string): Promise<User | null> {
    const user = await this.getUserByEmail(email);
    if (!user) return null;
    
    const isValid = await bcrypt.compare(password, user.passwordHash);
    return isValid ? user : null;
  }

  // Upload operations
  async createVideoUpload(upload: InsertVideoUpload): Promise<VideoUpload> {
    const id = randomUUID();
    const fullUpload: VideoUpload = {
      ...upload,
      id,
      uploadStatus: upload.uploadStatus || "uploading",
      uploadProgress: upload.uploadProgress || 0,
      videoId: upload.videoId || null,
      errorMessage: upload.errorMessage || null,
      uploadUrl: upload.uploadUrl || null,
      createdAt: new Date(),
      updatedAt: new Date()
    };
    this.uploads.set(id, fullUpload);
    return fullUpload;
  }

  async getVideoUpload(id: string): Promise<VideoUpload | undefined> {
    return this.uploads.get(id);
  }

  async updateVideoUpload(id: string, updates: Partial<InsertVideoUpload>): Promise<VideoUpload | undefined> {
    const upload = this.uploads.get(id);
    if (!upload) return undefined;

    const updatedUpload: VideoUpload = {
      ...upload,
      ...updates,
      updatedAt: new Date()
    };
    this.uploads.set(id, updatedUpload);
    return updatedUpload;
  }

  async getUserVideoUploads(userId: string): Promise<VideoUpload[]> {
    return Array.from(this.uploads.values())
      .filter(upload => upload.userId === userId)
      .sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime());
  }

  // Category operations
  async getCategories(): Promise<Category[]> {
    return Array.from(this.categoriesMap.values())
      .sort((a, b) => a.name.localeCompare(b.name));
  }

  async createCategory(category: InsertCategory): Promise<Category> {
    const id = randomUUID();
    const fullCategory: Category = {
      ...category,
      id,
      description: category.description || null,
      color: category.color || "#000000",
      createdAt: new Date()
    };
    this.categoriesMap.set(id, fullCategory);
    return fullCategory;
  }

  async updateCategory(id: string, updates: Partial<InsertCategory>): Promise<Category | undefined> {
    const category = this.categoriesMap.get(id);
    if (!category) return undefined;

    const updatedCategory: Category = {
      ...category,
      ...updates
    };
    this.categoriesMap.set(id, updatedCategory);
    return updatedCategory;
  }

  async deleteCategory(id: string): Promise<boolean> {
    return this.categoriesMap.delete(id);
  }

  // Tag operations
  async getTags(): Promise<Tag[]> {
    return Array.from(this.tagsMap.values())
      .sort((a, b) => b.useCount - a.useCount);
  }

  async getPopularTags(limit = 10): Promise<Tag[]> {
    return this.getTags().then(tags => tags.slice(0, limit));
  }

  async createTag(tag: InsertTag): Promise<Tag> {
    const id = randomUUID();
    const fullTag: Tag = {
      ...tag,
      id,
      useCount: tag.useCount ?? 0,
      createdAt: new Date()
    };
    this.tagsMap.set(id, fullTag);
    return fullTag;
  }

  async incrementTagUse(name: string): Promise<void> {
    const tag = Array.from(this.tagsMap.values()).find(t => t.name === name);
    if (tag) {
      tag.useCount += 1;
      this.tagsMap.set(tag.id, tag);
    }
  }
}

// Use Bunny.net storage if API keys are available, otherwise fallback to memory storage
class BunnyStorage implements IStorage {
  private bunnyService: BunnyService;
  private faceDataCache: Map<string, { faceCenterPosition?: string; facesDetected?: number; faceConfidence?: number }> = new Map();

  constructor() {
    this.bunnyService = new BunnyService();
  }

  async getVideos(limit = 20, offset = 0, search?: string): Promise<Video[]> {
    console.log(`Fetching videos from cache: limit=${limit}, offset=${offset}, search=${search}`);
    const result = videoSyncService.getVideos(limit, offset, search);
    console.log(`Returning ${result.videos.length} videos from cache (age: ${result.cacheAge}ms)`);
    
    // Apply face detection data from cache
    return result.videos.map(video => {
      const faceData = this.faceDataCache.get(video.id);
      return faceData ? { ...video, ...faceData } : video;
    });
  }

  async getVideo(id: string): Promise<Video | undefined> {
    // Try cache first for faster loading
    const cachedVideo = videoSyncService.getVideos(100, 0).videos.find(v => v.id === id);
    if (cachedVideo) {
      // Apply face detection data from cache
      const faceData = this.faceDataCache.get(cachedVideo.id);
      return faceData ? { ...cachedVideo, ...faceData } : cachedVideo;
    }

    // Fallback to direct API call
    try {
      const video = await this.bunnyService.getVideo(id);
      if (!video) return undefined;

      // Apply face detection data from cache
      const faceData = this.faceDataCache.get(video.id);
      return faceData ? { ...video, ...faceData } : video;
    } catch (error) {
      console.error(`Error fetching video ${id} from Bunny:`, error);
      return undefined;
    }
  }

  async createVideo(video: InsertVideo): Promise<Video> {
    throw new Error("Creating videos is not supported with Bunny.net integration");
  }

  async updateVideo(id: string, updates: UpdateVideo): Promise<Video | undefined> {
    // For admin edits, allow all metadata updates
    // Face detection and views require special handling but metadata can be updated
    
    try {
      // Try to update in database first
      let dbUpdateSuccessful = false;
      try {
        console.log(`Updating video ${id} with data:`, JSON.stringify(updates, null, 2));
        await db.update(videos).set({...updates, updatedAt: new Date()}).where(eq(videos.id, id));
        console.log(`Successfully updated video ${id} in database`);
        dbUpdateSuccessful = true;
      } catch (dbError) {
        console.warn(`Database update failed for video ${id}, using cache:`, dbError);
      }
      
      // If database update was successful, force video sync refresh
      if (dbUpdateSuccessful) {
        try {
          // Force videoSyncService to refresh this specific video
          console.log(`Updated video ${id} in database, forcing cache refresh`);
          videoSyncService.forceVideoRefresh(id);
        } catch (cacheError) {
          console.warn('Failed to update local cache after video update:', cacheError);
        }
      }
      
      // Update face detection data in cache if present
      const faceFields = ['faceCenterPosition', 'facesDetected', 'faceConfidence'];
      const faceData = Object.fromEntries(
        Object.entries(updates).filter(([key]) => faceFields.includes(key))
      );
      if (Object.keys(faceData).length > 0) {
        const existing = this.faceDataCache.get(id) || {};
        this.faceDataCache.set(id, { ...existing, ...faceData });
      }
      
      
      // Return updated video
      return await this.getVideo(id);
    } catch (error) {
      console.error(`Error updating video ${id}:`, error);
      throw error;
    }
  }

  async deleteVideo(id: string): Promise<boolean> {
    throw new Error("Deleting videos is not supported with Bunny.net integration");
  }

  // User operations (not supported with Bunny.net)
  async getUser(id: string): Promise<User | undefined> {
    throw new Error("User operations are not supported with Bunny.net integration");
  }

  async getUserByEmail(email: string): Promise<User | undefined> {
    throw new Error("User operations are not supported with Bunny.net integration");
  }

  async getUserByUsername(username: string): Promise<User | undefined> {
    throw new Error("User operations are not supported with Bunny.net integration");
  }

  async createUser(user: InsertUser): Promise<User> {
    throw new Error("User operations are not supported with Bunny.net integration");
  }

  async updateUser(id: string, updates: Partial<InsertUser>): Promise<User | undefined> {
    throw new Error("User operations are not supported with Bunny.net integration");
  }

  async upsertUser(userData: any): Promise<User> {
    throw new Error("User operations are not supported with Bunny.net integration");
  }

  async getAllUsers(limit?: number, offset?: number, search?: string): Promise<User[]> {
    throw new Error("User operations are not supported with Bunny.net integration");
  }

  async getUserCount(search?: string): Promise<number> {
    throw new Error("User operations are not supported with Bunny.net integration");
  }

  async validateUserPassword(email: string, password: string): Promise<User | null> {
    throw new Error("User operations are not supported with Bunny.net integration");
  }

  // Upload operations (not supported with Bunny.net)
  async createVideoUpload(upload: InsertVideoUpload): Promise<VideoUpload> {
    throw new Error("Upload operations are not supported with Bunny.net integration");
  }

  async getVideoUpload(id: string): Promise<VideoUpload | undefined> {
    throw new Error("Upload operations are not supported with Bunny.net integration");
  }

  async updateVideoUpload(id: string, updates: Partial<InsertVideoUpload>): Promise<VideoUpload | undefined> {
    throw new Error("Upload operations are not supported with Bunny.net integration");
  }

  async getUserVideoUploads(userId: string): Promise<VideoUpload[]> {
    throw new Error("Upload operations are not supported with Bunny.net integration");
  }

  // Category operations (not supported with Bunny.net)
  async getCategories(): Promise<Category[]> {
    throw new Error("Category operations are not supported with Bunny.net integration");
  }

  async createCategory(category: InsertCategory): Promise<Category> {
    throw new Error("Category operations are not supported with Bunny.net integration");
  }

  async updateCategory(id: string, updates: Partial<InsertCategory>): Promise<Category | undefined> {
    throw new Error("Category operations are not supported with Bunny.net integration");
  }

  async deleteCategory(id: string): Promise<boolean> {
    throw new Error("Category operations are not supported with Bunny.net integration");
  }

  // Tag operations (not supported with Bunny.net)
  async getTags(): Promise<Tag[]> {
    throw new Error("Tag operations are not supported with Bunny.net integration");
  }

  async getPopularTags(limit = 10): Promise<Tag[]> {
    throw new Error("Tag operations are not supported with Bunny.net integration");
  }

  async createTag(tag: InsertTag): Promise<Tag> {
    throw new Error("Tag operations are not supported with Bunny.net integration");
  }

  async incrementTagUse(name: string): Promise<void> {
    throw new Error("Tag operations are not supported with Bunny.net integration");
  }

  async updateVideoViews(id: string): Promise<void> {
    // Views are tracked automatically by Bunny.net CDN when videos are played
    // No additional tracking needed - views are fetched fresh every 5 minutes from Bunny.net API
    console.log(`Video ${id} played - views tracked by Bunny.net CDN`);
  }

  async getVideoCount(search?: string): Promise<number> {
    try {
      // Get the total from Bunny API directly for better performance
      const { total } = await this.bunnyService.getVideos(1, 1);
      
      // If no search, return total count
      if (!search) {
        return total;
      }
      
      // For search, we need to get videos and count filtered results
      // This is expensive but needed for accurate search counts
      const { videos } = await this.bunnyService.getVideos(1, 1000);
      const searchLower = search.toLowerCase();
      const filteredCount = videos.filter(video => 
        video.title.toLowerCase().includes(searchLower) ||
        (video.description && video.description.toLowerCase().includes(searchLower))
      ).length;
      
      return filteredCount;
    } catch (error) {
      console.error('Error getting video count from Bunny:', error);
      return 0;
    }
  }
}

// Hybrid storage implementation that uses Bunny.net for videos and Database for users
export class HybridStorage implements IStorage {
  private bunnyStorage: BunnyStorage;
  private databaseStorage: DatabaseStorage;

  constructor() {
    this.bunnyStorage = new BunnyStorage();
    this.databaseStorage = new DatabaseStorage();
  }

  // Video operations - use Bunny.net
  async getVideos(limit?: number, offset?: number, search?: string): Promise<Video[]> {
    return this.bunnyStorage.getVideos(limit, offset, search);
  }

  async getVideo(id: string): Promise<Video | undefined> {
    return this.bunnyStorage.getVideo(id);
  }

  async createVideo(video: InsertVideo): Promise<Video> {
    return this.bunnyStorage.createVideo(video);
  }

  async updateVideo(id: string, video: UpdateVideo): Promise<Video | undefined> {
    // Save changes ONLY to PostgreSQL database - do not update Bunny cache
    return this.databaseStorage.updateVideo(id, video);
  }

  async updateVideoViews(id: string): Promise<void> {
    return this.bunnyStorage.updateVideoViews(id);
  }

  async getVideoCount(search?: string): Promise<number> {
    return this.bunnyStorage.getVideoCount(search);
  }

  async deleteVideo(id: string): Promise<boolean> {
    return this.bunnyStorage.deleteVideo(id);
  }

  // User operations - use Database
  async getUser(id: string): Promise<User | undefined> {
    return this.databaseStorage.getUser(id);
  }

  async getUserByEmail(email: string): Promise<User | undefined> {
    return this.databaseStorage.getUserByEmail(email);
  }

  async getUserByUsername(username: string): Promise<User | undefined> {
    return this.databaseStorage.getUserByUsername(username);
  }

  async createUser(user: InsertUser): Promise<User> {
    return this.databaseStorage.createUser(user);
  }

  async updateUser(id: string, user: Partial<InsertUser>): Promise<User | undefined> {
    return this.databaseStorage.updateUser(id, user);
  }

  async upsertUser(user: any): Promise<User> {
    return this.databaseStorage.upsertUser(user);
  }

  async getAllUsers(limit?: number, offset?: number, search?: string): Promise<User[]> {
    return this.databaseStorage.getAllUsers(limit, offset, search);
  }

  async getUserCount(search?: string): Promise<number> {
    return this.databaseStorage.getUserCount(search);
  }

  async validateUserPassword(email: string, password: string): Promise<User | null> {
    return this.databaseStorage.validateUserPassword(email, password);
  }

  // Upload operations - use Database
  async createVideoUpload(upload: InsertVideoUpload): Promise<VideoUpload> {
    return this.databaseStorage.createVideoUpload(upload);
  }

  async getVideoUpload(id: string): Promise<VideoUpload | undefined> {
    return this.databaseStorage.getVideoUpload(id);
  }

  async updateVideoUpload(id: string, upload: Partial<InsertVideoUpload>): Promise<VideoUpload | undefined> {
    return this.databaseStorage.updateVideoUpload(id, upload);
  }

  async getUserVideoUploads(userId: string): Promise<VideoUpload[]> {
    return this.databaseStorage.getUserVideoUploads(userId);
  }

  // Category operations - use Database
  async getCategories(): Promise<Category[]> {
    return this.databaseStorage.getCategories();
  }

  async createCategory(category: InsertCategory): Promise<Category> {
    return this.databaseStorage.createCategory(category);
  }

  async updateCategory(id: string, category: Partial<InsertCategory>): Promise<Category | undefined> {
    return this.databaseStorage.updateCategory(id, category);
  }

  async deleteCategory(id: string): Promise<boolean> {
    return this.databaseStorage.deleteCategory(id);
  }

  // Tag operations - use Database
  async getTags(): Promise<Tag[]> {
    return this.databaseStorage.getTags();
  }

  async getPopularTags(limit?: number): Promise<Tag[]> {
    return this.databaseStorage.getPopularTags(limit);
  }

  async createTag(tag: InsertTag): Promise<Tag> {
    return this.databaseStorage.createTag(tag);
  }

  async incrementTagUse(name: string): Promise<void> {
    return this.databaseStorage.incrementTagUse(name);
  }
}

// Storage selection logic - use hybrid approach when both Bunny.net and Database are available
let storage: IStorage;

const hasDatabase = process.env.DATABASE_URL;
const hasBunnyConfig = process.env.BUNNY_API_KEY && process.env.BUNNY_LIBRARY_ID && process.env.BUNNY_HOSTNAME;

// Use hybrid storage when both Bunny.net and Database are available
if (hasBunnyConfig && hasDatabase) {
  try {
    storage = new HybridStorage();
    console.log('✅ Using Hybrid storage (Bunny.net for videos + Database for users)');
  } catch (error) {
    console.error('❌ Failed to initialize Hybrid storage:', error);
    console.log('📁 Falling back to memory storage');
    storage = new MemStorage();
  }
}
// Prioritize Bunny.net storage for video content only
else if (hasBunnyConfig) {
  try {
    storage = new BunnyStorage();
    console.log('✅ Using Bunny.net storage with library ID:', process.env.BUNNY_LIBRARY_ID);
  } catch (error) {
    console.error('❌ Failed to initialize Bunny.net storage:', error);
    console.log('📁 Falling back to memory storage');
    storage = new MemStorage();
  }
} else if (hasDatabase) {
  try {
    storage = new DatabaseStorage();
    console.log('✅ Using PostgreSQL database storage');
  } catch (error) {
    console.error('❌ Failed to initialize database storage:', error);
    console.log('📁 Falling back to memory storage');
    storage = new MemStorage();
  }
} else {
  console.log('📁 Using memory storage (no database or Bunny.net config found)');
  storage = new MemStorage();
}

export { storage };
