import type { Express, Request, Response } from "express";
import { createServer, type Server } from "http";
import { storage } from "./storage";
import { z } from "zod";
import { NextFunction } from "express";

import {
  insertUserSchema,
  insertAuthorSchema,
  insertWorkSchema,
  insertTranslationSchema,
  insertTagSchema,
  insertBookmarkSchema,
  insertCommentSchema,
  insertLikeSchema,
  insertCollectionSchema,
  insertCollectionItemSchema,
  insertTimelineEventSchema,
  insertBlogPostSchema,
  insertReadingProgressSchema,
  Comment
} from "@shared/schema";

export async function registerRoutes(app: Express): Promise<Server> {
  // Users routes
  app.get("/api/users/:id", async (req: Request, res: Response) => {
    const user = await storage.getUser(Number(req.params.id));
    if (!user) {
      return res.status(404).json({ message: "User not found" });
    }
    
    // Remove sensitive data
    const { password, ...safeUser } = user;
    res.json(safeUser);
  });
  
  app.post("/api/users", async (req: Request, res: Response) => {
    try {
      const userData = insertUserSchema.parse(req.body);
      const user = await storage.createUser(userData);
      const { password, ...safeUser } = user;
      res.status(201).json(safeUser);
    } catch (error) {
      if (error instanceof z.ZodError) {
        return res.status(400).json({ message: error.errors });
      }
      res.status(500).json({ message: "Failed to create user" });
    }
  });
  
  // Authors routes
  app.get("/api/authors", async (req: Request, res: Response) => {
    const limit = req.query.limit ? Number(req.query.limit) : undefined;
    const offset = req.query.offset ? Number(req.query.offset) : undefined;
    const authors = await storage.getAuthors(limit, offset);
    res.json(authors);
  });
  
  app.get("/api/authors/:slug", async (req: Request, res: Response) => {
    const author = await storage.getAuthorBySlug(req.params.slug);
    if (!author) {
      return res.status(404).json({ message: "Author not found" });
    }
    res.json(author);
  });
  
  app.post("/api/authors", async (req: Request, res: Response) => {
    try {
      const authorData = insertAuthorSchema.parse(req.body);
      const author = await storage.createAuthor(authorData);
      res.status(201).json(author);
    } catch (error) {
      if (error instanceof z.ZodError) {
        return res.status(400).json({ message: error.errors });
      }
      res.status(500).json({ message: "Failed to create author" });
    }
  });
  
  // Works routes
  app.get("/api/works", async (req: Request, res: Response) => {
    const limit = req.query.limit ? Number(req.query.limit) : undefined;
    const offset = req.query.offset ? Number(req.query.offset) : undefined;
    const works = await storage.getWorks(limit, offset);
    res.json(works);
  });
  
  app.get("/api/works/:slug", async (req: Request, res: Response) => {
    const work = await storage.getWorkBySlug(req.params.slug);
    if (!work) {
      return res.status(404).json({ message: "Work not found" });
    }
    
    // Get the author
    const author = await storage.getAuthorById(work.authorId);
    
    // Get the tags
    const tags = await storage.getWorkTags(work.id);
    
    res.json({ ...work, author, tags });
  });
  
  app.get("/api/authors/:slug/works", async (req: Request, res: Response) => {
    const author = await storage.getAuthorBySlug(req.params.slug);
    if (!author) {
      return res.status(404).json({ message: "Author not found" });
    }
    
    const works = await storage.getWorksByAuthorId(author.id);
    
    // Get tags for each work
    const worksWithTags = await Promise.all(works.map(async (work) => {
      const tags = await storage.getWorkTags(work.id);
      return { ...work, tags };
    }));
    
    res.json(worksWithTags);
  });
  
  app.post("/api/works", async (req: Request, res: Response) => {
    try {
      const workData = insertWorkSchema.parse(req.body);
      const work = await storage.createWork(workData);
      res.status(201).json(work);
    } catch (error) {
      if (error instanceof z.ZodError) {
        return res.status(400).json({ message: error.errors });
      }
      res.status(500).json({ message: "Failed to create work" });
    }
  });
  
  // Translations routes
  app.get("/api/works/:slug/translations", async (req: Request, res: Response) => {
    const work = await storage.getWorkBySlug(req.params.slug);
    if (!work) {
      return res.status(404).json({ message: "Work not found" });
    }
    
    const translations = await storage.getTranslations(work.id);
    res.json(translations);
  });
  
  app.get("/api/translations/:id", async (req: Request, res: Response) => {
    const translation = await storage.getTranslationById(Number(req.params.id));
    if (!translation) {
      return res.status(404).json({ message: "Translation not found" });
    }
    res.json(translation);
  });
  
  app.post("/api/translations", async (req: Request, res: Response) => {
    try {
      const translationData = insertTranslationSchema.parse(req.body);
      const translation = await storage.createTranslation(translationData);
      res.status(201).json(translation);
    } catch (error) {
      if (error instanceof z.ZodError) {
        return res.status(400).json({ message: error.errors });
      }
      res.status(500).json({ message: "Failed to create translation" });
    }
  });
  
  // Tags routes
  app.get("/api/tags", async (req: Request, res: Response) => {
    const tags = await storage.getTags();
    res.json(tags);
  });
  
  app.get("/api/tags/type/:type", async (req: Request, res: Response) => {
    const tags = await storage.getTagsByType(req.params.type);
    res.json(tags);
  });
  
  app.post("/api/tags", async (req: Request, res: Response) => {
    try {
      const tagData = insertTagSchema.parse(req.body);
      const tag = await storage.createTag(tagData);
      res.status(201).json(tag);
    } catch (error) {
      if (error instanceof z.ZodError) {
        return res.status(400).json({ message: error.errors });
      }
      res.status(500).json({ message: "Failed to create tag" });
    }
  });
  
  app.post("/api/works/:workId/tags/:tagId", async (req: Request, res: Response) => {
    const workId = Number(req.params.workId);
    const tagId = Number(req.params.tagId);
    
    const work = await storage.getWorkById(workId);
    if (!work) {
      return res.status(404).json({ message: "Work not found" });
    }
    
    const tag = await storage.tags;
    if (!tag) {
      return res.status(404).json({ message: "Tag not found" });
    }
    
    await storage.addTagToWork(workId, tagId);
    res.status(201).json({ workId, tagId });
  });
  
  // Bookmarks routes
  app.get("/api/users/:userId/bookmarks", async (req: Request, res: Response) => {
    const userId = Number(req.params.userId);
    const bookmarks = await storage.getBookmarksByUserId(userId);
    
    // Get work details for each bookmark
    const bookmarksWithWorks = await Promise.all(bookmarks.map(async (bookmark) => {
      const work = await storage.getWorkById(bookmark.workId);
      return { ...bookmark, work };
    }));
    
    res.json(bookmarksWithWorks);
  });
  
  app.post("/api/bookmarks", async (req: Request, res: Response) => {
    try {
      const bookmarkData = insertBookmarkSchema.parse(req.body);
      
      // Check if bookmark already exists
      const existing = await storage.getBookmarkByUserAndWork(
        bookmarkData.userId, 
        bookmarkData.workId
      );
      
      if (existing) {
        return res.status(400).json({ message: "Bookmark already exists" });
      }
      
      const bookmark = await storage.createBookmark(bookmarkData);
      res.status(201).json(bookmark);
    } catch (error) {
      if (error instanceof z.ZodError) {
        return res.status(400).json({ message: error.errors });
      }
      res.status(500).json({ message: "Failed to create bookmark" });
    }
  });
  
  app.delete("/api/bookmarks/:id", async (req: Request, res: Response) => {
    const id = Number(req.params.id);
    await storage.deleteBookmark(id);
    res.status(204).send();
  });
  
  // Comments routes
  app.get("/api/works/:slug/comments", async (req: Request, res: Response) => {
    const work = await storage.getWorkBySlug(req.params.slug);
    if (!work) {
      return res.status(404).json({ message: "Work not found" });
    }
    
    const comments = await storage.getCommentsByWorkId(work.id);
    
    // Get user details for each comment
    const commentsWithUsers = await Promise.all(comments.map(async (comment) => {
      const user = await storage.getUser(comment.userId);
      if (!user) return comment;
      const { password, ...safeUser } = user;
      return { ...comment, user: safeUser };
    }));
    
    res.json(commentsWithUsers);
  });
  
  app.post("/api/comments", async (req: Request, res: Response) => {
    try {
      const commentData = insertCommentSchema.parse(req.body);
      const comment = await storage.createComment(commentData);
      
      // Get user details
      const user = await storage.getUser(comment.userId);
      if (user) {
        const { password, ...safeUser } = user;
        res.status(201).json({ ...comment, user: safeUser });
      } else {
        res.status(201).json(comment);
      }
    } catch (error) {
      if (error instanceof z.ZodError) {
        return res.status(400).json({ message: error.errors });
      }
      res.status(500).json({ message: "Failed to create comment" });
    }
  });
  
  // Likes routes
  app.post("/api/likes", async (req: Request, res: Response) => {
    try {
      const likeData = insertLikeSchema.parse(req.body);
      const like = await storage.createLike(likeData);
      res.status(201).json(like);
    } catch (error) {
      if (error instanceof z.ZodError) {
        return res.status(400).json({ message: error.errors });
      }
      res.status(500).json({ message: "Failed to create like" });
    }
  });
  
  app.delete("/api/likes/:id", async (req: Request, res: Response) => {
    const id = Number(req.params.id);
    await storage.deleteLike(id);
    res.status(204).send();
  });
  
  app.get("/api/likes/:entityType/:entityId", async (req: Request, res: Response) => {
    const entityType = req.params.entityType;
    const entityId = Number(req.params.entityId);
    const likes = await storage.getLikesByEntity(entityType, entityId);
    res.json(likes);
  });
  
  // Collections routes
  app.get("/api/collections", async (req: Request, res: Response) => {
    const limit = req.query.limit ? Number(req.query.limit) : undefined;
    const offset = req.query.offset ? Number(req.query.offset) : undefined;
    const collections = await storage.getCollections(limit, offset);
    res.json(collections);
  });
  
  app.get("/api/collections/:slug", async (req: Request, res: Response) => {
    const collection = await storage.getCollectionBySlug(req.params.slug);
    if (!collection) {
      return res.status(404).json({ message: "Collection not found" });
    }
    
    // Get collection items
    const items = await storage.getCollectionItems(collection.id);
    
    // Get work details for each item
    const itemsWithWorks = await Promise.all(items.map(async (item) => {
      const work = await storage.getWorkById(item.workId);
      return { ...item, work };
    }));
    
    res.json({ ...collection, items: itemsWithWorks });
  });
  
  app.get("/api/users/:userId/collections", async (req: Request, res: Response) => {
    const userId = Number(req.params.userId);
    const collections = await storage.getCollectionsByUserId(userId);
    res.json(collections);
  });
  
  app.post("/api/collections", async (req: Request, res: Response) => {
    try {
      const collectionData = insertCollectionSchema.parse(req.body);
      const collection = await storage.createCollection(collectionData);
      res.status(201).json(collection);
    } catch (error) {
      if (error instanceof z.ZodError) {
        return res.status(400).json({ message: error.errors });
      }
      res.status(500).json({ message: "Failed to create collection" });
    }
  });
  
  app.post("/api/collections/:collectionId/works", async (req: Request, res: Response) => {
    try {
      const collectionId = Number(req.params.collectionId);
      const collection = await storage.getCollectionById(collectionId);
      if (!collection) {
        return res.status(404).json({ message: "Collection not found" });
      }
      
      const itemData = {
        ...insertCollectionItemSchema.parse(req.body),
        collectionId
      };
      
      const item = await storage.addWorkToCollection(itemData);
      res.status(201).json(item);
    } catch (error) {
      if (error instanceof z.ZodError) {
        return res.status(400).json({ message: error.errors });
      }
      res.status(500).json({ message: "Failed to add work to collection" });
    }
  });
  
  // Timeline events routes
  app.get("/api/authors/:slug/timeline", async (req: Request, res: Response) => {
    const author = await storage.getAuthorBySlug(req.params.slug);
    if (!author) {
      return res.status(404).json({ message: "Author not found" });
    }
    
    const events = await storage.getTimelineEventsByAuthorId(author.id);
    res.json(events);
  });
  
  app.post("/api/timeline-events", async (req: Request, res: Response) => {
    try {
      const eventData = insertTimelineEventSchema.parse(req.body);
      const event = await storage.createTimelineEvent(eventData);
      res.status(201).json(event);
    } catch (error) {
      if (error instanceof z.ZodError) {
        return res.status(400).json({ message: error.errors });
      }
      res.status(500).json({ message: "Failed to create timeline event" });
    }
  });
  
  // Blog posts routes
  app.get("/api/blog", async (req: Request, res: Response) => {
    const limit = req.query.limit ? Number(req.query.limit) : undefined;
    const offset = req.query.offset ? Number(req.query.offset) : undefined;
    
    try {
      // Get basic blog posts
      const posts = await storage.getBlogPosts(limit, offset);
      
      // Enhance each post with author details, comment count, and like count
      const enhancedPosts = await Promise.all(posts.map(async (post) => {
        const author = post.authorId ? await storage.getUser(post.authorId) : undefined;
        const authorInfo = author ? {
          id: author.id,
          displayName: author.displayName,
          avatar: author.avatar
        } : undefined;

        // Get comment count
        const comments = await storage.getCommentsByEntityId('blogPost', post.id);
        const commentCount = comments.length;
        
        // Get like count
        const likes = await storage.getLikesByEntity('blogPost', post.id);
        const likeCount = likes.length;
        
        // Get tags for the post
        const tags = await storage.getBlogPostTags(post.id);
        
        return {
          ...post,
          author: authorInfo,
          commentCount,
          likeCount,
          tags
        };
      }));
      
      res.json(enhancedPosts);
    } catch (error) {
      console.error("Error fetching blog posts:", error);
      res.status(500).json({ message: "Failed to fetch blog posts" });
    }
  });
  
  app.get("/api/blog/:slug", async (req: Request, res: Response) => {
    try {
      const post = await storage.getBlogPostBySlug(req.params.slug);
      if (!post) {
        return res.status(404).json({ message: "Blog post not found" });
      }
      
      // Get author details
      const author = post.authorId ? await storage.getUser(post.authorId) : undefined;
      
      // Remove password from author if it exists
      const authorInfo = author ? {
        ...author,
        password: undefined
      } : undefined;
      
      // Get comments for the post
      const comments = await storage.getCommentsByEntityId('blogPost', post.id);
      console.log('Blog post comments for', post.id, ':', comments);
      
      // Get enhanced comments with user info
      const enhancedComments = await Promise.all(comments.map(async (comment: Comment) => {
        // Create enhanced comment object
        interface EnhancedComment extends Comment {
          user?: {
            id: number;
            displayName: string | null;
            avatar: string | null;
            role: string;
          };
        }
        
        const user = await storage.getUser(comment.userId);
        const enhancedComment: EnhancedComment = {
          ...comment,
          user: user ? {
            id: user.id,
            displayName: user.displayName,
            avatar: user.avatar,
            role: user.role
          } : undefined
        };
        
        return enhancedComment;
      }));
      
      // Get like count
      const likes = await storage.getLikesByEntity('blogPost', post.id);
      const likeCount = likes.length;
      
      // Get tags for the post
      const tags = await storage.getBlogPostTags(post.id);
      console.log('Blog post tags for', post.id, ':', tags);
      
      res.json({
        ...post,
        author: authorInfo,
        comments: enhancedComments,
        commentCount: comments.length,
        likeCount,
        tags
      });
    } catch (error) {
      console.error("Error fetching blog post:", error);
      res.status(500).json({ message: "Failed to fetch blog post" });
    }
  });
  
  app.post("/api/blog", async (req: Request, res: Response) => {
    try {
      const postData = insertBlogPostSchema.parse(req.body);
      const post = await storage.createBlogPost(postData);
      res.status(201).json(post);
    } catch (error) {
      if (error instanceof z.ZodError) {
        return res.status(400).json({ message: error.errors });
      }
      res.status(500).json({ message: "Failed to create blog post" });
    }
  });
  
  // Reading progress routes
  app.get("/api/reading-progress/:userId/:workId", async (req: Request, res: Response) => {
    const userId = Number(req.params.userId);
    const workId = Number(req.params.workId);
    const translationId = req.query.translationId ? Number(req.query.translationId) : undefined;
    
    const progress = await storage.getReadingProgress(userId, workId, translationId);
    if (!progress) {
      return res.status(404).json({ message: "Reading progress not found" });
    }
    
    res.json(progress);
  });
  
  app.post("/api/reading-progress", async (req: Request, res: Response) => {
    try {
      const progressData = insertReadingProgressSchema.parse(req.body);
      const progress = await storage.updateReadingProgress(progressData);
      res.status(201).json(progress);
    } catch (error) {
      if (error instanceof z.ZodError) {
        return res.status(400).json({ message: error.errors });
      }
      res.status(500).json({ message: "Failed to update reading progress" });
    }
  });
  
  // Search routes
  app.get("/api/search", async (req: Request, res: Response) => {
    const query = req.query.q as string;
    if (!query) {
      return res.status(400).json({ message: "Search query is required" });
    }
    
    const workLimit = req.query.workLimit ? Number(req.query.workLimit) : 5;
    const authorLimit = req.query.authorLimit ? Number(req.query.authorLimit) : 3;
    
    const works = await storage.searchWorks(query, workLimit);
    const authors = await storage.searchAuthors(query, authorLimit);
    
    res.json({ works, authors });
  });
  
  // Filter routes
  app.get("/api/filter", async (req: Request, res: Response) => {
    const language = req.query.language as string | undefined;
    const type = req.query.type as string | undefined;
    const yearStart = req.query.yearStart ? Number(req.query.yearStart) : undefined;
    const yearEnd = req.query.yearEnd ? Number(req.query.yearEnd) : undefined;
    
    let tags: number[] = [];
    if (req.query.tags) {
      tags = (req.query.tags as string).split(',').map(Number);
    }
    
    const works = await storage.filterWorks({
      language,
      type,
      yearStart,
      yearEnd,
      tags: tags.length > 0 ? tags : undefined
    });
    
    res.json(works);
  });

  // Analysis results routes
  app.get("/api/works/:slug/analysis", async (req: Request, res: Response) => {
    try {
      const work = await storage.getWorkBySlug(req.params.slug);
      if (!work) {
        return res.status(404).json({ message: "Work not found" });
      }
      
      const results = await storage.getAnalysisResultsByWorkId(work.id);
      res.json(results);
    } catch (error) {
      console.error("Error fetching analysis results:", error);
      res.status(500).json({ message: "Failed to fetch analysis results" });
    }
  });
  
  app.get("/api/works/:slug/analysis/:type", async (req: Request, res: Response) => {
    try {
      const work = await storage.getWorkBySlug(req.params.slug);
      if (!work) {
        return res.status(404).json({ message: "Work not found" });
      }
      
      const result = await storage.getAnalysisResultByWorkAndType(work.id, req.params.type);
      if (!result) {
        return res.status(404).json({ message: "Analysis result not found" });
      }
      
      res.json(result);
    } catch (error) {
      console.error("Error fetching analysis result:", error);
      res.status(500).json({ message: "Failed to fetch analysis result" });
    }
  });
  
  app.post("/api/analysis", async (req: Request, res: Response) => {
    try {
      // Validate request body using zod
      const analysisData = z.object({
        workId: z.number(),
        type: z.string(),
        data: z.any()
      }).parse(req.body);
      
      const result = await storage.createAnalysisResult(analysisData);
      res.status(201).json(result);
    } catch (error) {
      if (error instanceof z.ZodError) {
        return res.status(400).json({ message: error.errors });
      }
      console.error("Error creating analysis result:", error);
      res.status(500).json({ message: "Failed to create analysis result" });
    }
  });
  
  // Annotations routes
  app.get("/api/works/:slug/annotations", async (req: Request, res: Response) => {
    try {
      const work = await storage.getWorkBySlug(req.params.slug);
      if (!work) {
        return res.status(404).json({ message: "Work not found" });
      }
      
      const annotations = await storage.getAnnotationsByWorkId(work.id);
      
      // Enrich annotations with user information
      const enrichedAnnotations = await Promise.all(annotations.map(async (annotation) => {
        const user = await storage.getUser(annotation.userId);
        if (!user) return annotation;
        
        const { password, ...safeUser } = user;
        return { ...annotation, user: safeUser };
      }));
      
      res.json(enrichedAnnotations);
    } catch (error) {
      console.error("Error fetching annotations:", error);
      res.status(500).json({ message: "Failed to fetch annotations" });
    }
  });
  
  app.get("/api/works/:slug/line/:lineNumber/annotations", async (req: Request, res: Response) => {
    try {
      const work = await storage.getWorkBySlug(req.params.slug);
      if (!work) {
        return res.status(404).json({ message: "Work not found" });
      }
      
      const lineNumber = Number(req.params.lineNumber);
      const translationId = req.query.translationId ? Number(req.query.translationId) : undefined;
      
      const annotations = await storage.getAnnotationsByLine(
        work.id,
        lineNumber,
        translationId
      );
      
      // Enrich annotations with user information
      const enrichedAnnotations = await Promise.all(annotations.map(async (annotation) => {
        const user = await storage.getUser(annotation.userId);
        if (!user) return annotation;
        
        const { password, ...safeUser } = user;
        return { ...annotation, user: safeUser };
      }));
      
      res.json(enrichedAnnotations);
    } catch (error) {
      console.error("Error fetching line annotations:", error);
      res.status(500).json({ message: "Failed to fetch line annotations" });
    }
  });
  
  // Stats endpoints for the dashboard
  app.get("/api/stats/works", async (req: Request, res: Response) => {
    try {
      const works = await storage.getWorks(1000, 0);  // Get all works
      res.status(200).json({
        count: works.length
      });
    } catch (error) {
      console.error("Error fetching work stats:", error);
      res.status(500).json({ message: "Failed to fetch work statistics" });
    }
  });

  app.get("/api/stats/users", async (req: Request, res: Response) => {
    try {
      // In a real implementation, we would have a method to get the count directly
      // For now, we'll just return a hardcoded number or estimate
      const users = []; // We would need a getUsers function
      res.status(200).json({
        count: 5 // Hardcoded count from sampleData
      });
    } catch (error) {
      console.error("Error fetching user stats:", error);
      res.status(500).json({ message: "Failed to fetch user statistics" });
    }
  });

  app.get("/api/stats/blog", async (req: Request, res: Response) => {
    try {
      const posts = await storage.getBlogPosts(1000, 0);  // Get all blog posts
      res.status(200).json({
        count: posts.length
      });
    } catch (error) {
      console.error("Error fetching blog stats:", error);
      res.status(500).json({ message: "Failed to fetch blog statistics" });
    }
  });

  app.get("/api/stats/comments", async (req: Request, res: Response) => {
    try {
      // In a real implementation, we'd have a getComments function
      // For now, we'll just return a hardcoded number
      res.status(200).json({
        count: 12 // Example count
      });
    } catch (error) {
      console.error("Error fetching comment stats:", error);
      res.status(500).json({ message: "Failed to fetch comment statistics" });
    }
  });

  app.post("/api/annotations", async (req: Request, res: Response) => {
    try {
      // Validate request body using zod
      const annotationData = z.object({
        workId: z.number(),
        translationId: z.number().nullable().optional(),
        lineNumber: z.number().nullable().optional(),
        content: z.string(),
        type: z.string(),
        userId: z.number(),
        isOfficial: z.boolean()
      }).parse(req.body);
      
      const annotation = await storage.createAnnotation(annotationData);
      
      // Get user details
      const user = await storage.getUser(annotation.userId);
      if (user) {
        const { password, ...safeUser } = user;
        res.status(201).json({ ...annotation, user: safeUser });
      } else {
        res.status(201).json(annotation);
      }
    } catch (error) {
      if (error instanceof z.ZodError) {
        return res.status(400).json({ message: error.errors });
      }
      console.error("Error creating annotation:", error);
      res.status(500).json({ message: "Failed to create annotation" });
    }
  });

  const httpServer = createServer(app);
  return httpServer;
}
