import { Router } from 'express';
import { z } from 'zod';
import type { Database } from 'sql.js';
import { RecipeService } from '../services/RecipeService.js';

const createRecipeSchema = z.object({
  title: z.string().min(1, 'Title is required'),
  description: z.string().optional(),
  servings: z.number().int().positive().optional(),
  prep_time_minutes: z.number().int().positive().optional(),
  cook_time_minutes: z.number().int().positive().optional(),
  source_url: z.string().url().optional().or(z.literal('')),
  image_url: z.string().url().optional().or(z.literal('')),
  ingredients: z.array(z.object({
    quantity: z.string().optional(),
    unit: z.string().optional(),
    item: z.string().min(1, 'Ingredient required'),
    notes: z.string().optional(),
  })).min(1, 'At least one ingredient is required'),
  steps: z.array(z.object({
    instruction: z.string().min(1, 'Instruction required'),
  })).min(1, 'At least one step is required'),
  tagIds: z.array(z.number().int().positive()).optional(),
});

const updateRecipeSchema = z.object({
  title: z.string().min(1).optional(),
  description: z.string().optional().nullable(),
  servings: z.number().int().positive().optional().nullable(),
  prep_time_minutes: z.number().int().positive().optional().nullable(),
  cook_time_minutes: z.number().int().positive().optional().nullable(),
  source_url: z.string().url().optional().nullable().or(z.literal('')),
  image_url: z.string().url().optional().nullable().or(z.literal('')),
  ingredients: z.array(z.object({
    quantity: z.string().optional(),
    unit: z.string().optional(),
    item: z.string().min(1).optional(),
    notes: z.string().optional(),
  })).optional(),
  steps: z.array(z.object({ instruction: z.string().min(1).optional() })).optional(),
  tagIds: z.array(z.number().int().positive()).optional(),
});

const recipeFiltersSchema = z.object({
  search: z.string().optional(),
  offset: z.coerce.number().int().nonnegative().optional(),
  limit: z.coerce.number().int().positive().max(100).optional(),
  tagId: z.coerce.number().int().positive().optional(),
  tagIds: z.preprocess(
    (value) => {
      if (typeof value === 'string') {
        if (!value.trim()) return [];
        return value
          .split(',')
          .map((part) => Number(part.trim()))
          .filter((num) => Number.isInteger(num) && num > 0);
      }
      if (Array.isArray(value)) {
        return value
          .map((part) => Number(part))
          .filter((num) => Number.isInteger(num) && num > 0);
      }
      return undefined;
    },
    z.array(z.number().int().positive()).optional(),
  ),
});

export function createRecipeRoutes(db: Database): Router {
  const router = Router();
  const recipeService = new RecipeService(db);

  router.get('/', (req, res) => {
    try {
      const parsedFilters = recipeFiltersSchema.parse(req.query);
      const normalizedTagIds = parsedFilters.tagIds && parsedFilters.tagIds.length > 0
        ? parsedFilters.tagIds
        : parsedFilters.tagId
          ? [parsedFilters.tagId]
          : undefined;

      const filters = {
        ...parsedFilters,
        tagIds: normalizedTagIds,
      };

      const result = recipeService.list(filters);
      res.json({
        success: true,
        data: result.recipes,
        meta: {
          total: result.total,
          offset: filters.offset || 0,
          limit: filters.limit || 50,
        },
        error: null,
      });
    } catch (error) {
      if (error instanceof z.ZodError) {
        res.status(400).json({ success: false, data: null, error: error.errors });
      } else {
        res.status(500).json({ success: false, data: null, error: 'Internal server error' });
      }
    }
  });

  router.get('/:id', (req, res) => {
    try {
      const id = parseInt(req.params.id, 10);
      if (isNaN(id)) {
        res.status(400).json({ success: false, data: null, error: 'Invalid recipe ID' });
        return;
      }
      const recipe = recipeService.get(id);
      if (!recipe) {
        res.status(404).json({ success: false, data: null, error: 'Recipe not found' });
        return;
      }
      res.json({ success: true, data: recipe, error: null });
    } catch (error) {
      res.status(500).json({ success: false, data: null, error: 'Internal server error' });
    }
  });

  router.post('/', (req, res) => {
    try {
      const data = createRecipeSchema.parse(req.body);
      const recipe = recipeService.create(data);
      res.status(201).json({ success: true, data: recipe, error: null });
    } catch (error) {
      if (error instanceof z.ZodError) {
        res.status(400).json({ success: false, data: null, error: error.errors });
      } else if (error instanceof Error) {
        res.status(400).json({ success: false, data: null, error: error.message });
      } else {
        res.status(500).json({ success: false, data: null, error: 'Internal server error' });
      }
    }
  });

  router.put('/:id', (req, res) => {
    try {
      const id = parseInt(req.params.id, 10);
      if (isNaN(id)) {
        res.status(400).json({ success: false, data: null, error: 'Invalid recipe ID' });
        return;
      }
      const data = updateRecipeSchema.parse(req.body);
      const recipe = recipeService.update(id, data);
      if (!recipe) {
        res.status(404).json({ success: false, data: null, error: 'Recipe not found' });
        return;
      }
      res.json({ success: true, data: recipe, error: null });
    } catch (error) {
      if (error instanceof z.ZodError) {
        res.status(400).json({ success: false, data: null, error: error.errors });
      } else if (error instanceof Error) {
        res.status(400).json({ success: false, data: null, error: error.message });
      } else {
        res.status(500).json({ success: false, data: null, error: 'Internal server error' });
      }
    }
  });

  router.delete('/:id', (req, res) => {
    try {
      const id = parseInt(req.params.id, 10);
      if (isNaN(id)) {
        res.status(400).json({ success: false, data: null, error: 'Invalid recipe ID' });
        return;
      }
      const deleted = recipeService.delete(id);
      if (!deleted) {
        res.status(404).json({ success: false, data: null, error: 'Recipe not found' });
        return;
      }
      res.json({ success: true, data: true, error: null });
    } catch (error) {
      res.status(500).json({ success: false, data: null, error: 'Internal server error' });
    }
  });

  return router;
}
