/* $Id$ */

/**
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.manifoldcf.crawler.connectors.wiki.tests;

import org.eclipse.jetty.ee10.servlet.ServletHolder;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.ServerConnector;
import org.eclipse.jetty.ee10.servlet.ServletContextHandler;
import org.eclipse.jetty.util.thread.QueuedThreadPool;

import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

import java.io.*;
import java.util.*;

/** Mock wiki service */
public class MockWikiService
{
  Server server;
  WikiAPIServlet servlet;
    
  public MockWikiService(Class theResourceClass)
  {
    server = new Server(new QueuedThreadPool(35));
    ServerConnector connector = new ServerConnector(server);
    connector.setPort(8089);
    server.addConnector(connector);
    servlet = new WikiAPIServlet(theResourceClass);
    ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS);
    context.setContextPath("/w");
    server.setHandler(context);
    context.addServlet(new ServletHolder(servlet), "/api.php");
  }
    
  public void start() throws Exception
  {
    server.start();
  }
    
  public void stop() throws Exception
  {
    server.stop();
  }

  public void setResources(Map<String,String> checkResources,
    Map<String,String> listResources,
    Map<String,String> timestampQueryResources,
    Map<String,String> urlQueryResources,
    Map<String,String> docInfoQueryResources,
    String namespaceResource)
  {
    servlet.setResources(checkResources,listResources,timestampQueryResources,urlQueryResources,docInfoQueryResources,namespaceResource);
  }
  
  protected static String sortStuff(String input)
  {
    String[] ids = input.split("\\|");
    java.util.Arrays.sort(ids);
    StringBuilder sb = new StringBuilder();
    for (int i = 0 ; i < ids.length ; i++)
    {
      if (i > 0)
        sb.append("|");
      sb.append(ids[i]);
    }
    return sb.toString();
  }
  
  public static class WikiAPIServlet extends HttpServlet
  {
    protected Class theResourceClass;
    
    protected Map<String,String> checkResources = null;
    protected Map<String,String> listResources = null;
    protected Map<String,String> timestampQueryResources = null;
    protected Map<String,String> urlQueryResources = null;
    protected Map<String,String> docInfoQueryResources = null;
    protected String namespaceResource = null;
    
    public WikiAPIServlet(Class theResourceClass)
    {
      this.theResourceClass = theResourceClass;
    }
    
    public void setResources(Map<String,String> checkResources,
      Map<String,String> listResources,
      Map<String,String> timestampQueryResources,
      Map<String,String> urlQueryResources,
      Map<String,String> docInfoQueryResources,
      String namespaceResource)
    {
      this.checkResources = checkResources;
      this.listResources = listResources;
      this.timestampQueryResources = timestampQueryResources;
      this.urlQueryResources = urlQueryResources;
      this.docInfoQueryResources = docInfoQueryResources;
      this.namespaceResource = namespaceResource;
    }
    
    @Override
    public void service(HttpServletRequest req, HttpServletResponse res)
      throws IOException
    {
      String resourceName = null;
      
      String format = req.getParameter("format");
      if (!format.equals("xml"))
        throw new IOException("Format parameter incorrect: "+format);
      String action = req.getParameter("action");
      if (action == null || !action.equals("query"))
        throw new IOException("Action parameter incorrect: "+action);
      String list = req.getParameter("list");
      String prop = req.getParameter("prop");
      String siprop = req.getParameter("siprop");
      if (prop != null)
      {
	if (siprop != null || list != null)
	  throw new IOException("Cannot have both prop and list or siprop");
        String pageIds = req.getParameter("pageids");
        if (prop.equals("revisions"))
        {
          String rvprop = req.getParameter("rvprop");
          if (rvprop != null && rvprop.equals("timestamp"))
          {
            // Version query
            if (pageIds == null)
              throw new IOException("missing pageids parameter, required for version query");
            // Sort the pageIds
            pageIds = sortStuff(pageIds);
            resourceName = timestampQueryResources.get(pageIds);
            if (resourceName == null)
              throw new IOException("Could not find a matching resource for the timestamp parameters; pageids = '"+pageIds+"'");
          }
          else if (rvprop != null && rvprop.equals("user|comment|content|timestamp"))
          {
            // Doc info query
            if (pageIds == null)
              throw new IOException("missing pageids parameter, required for docinfo query");
            if (pageIds.indexOf("|") != -1)
              throw new IOException("cannot do more than one docinfo request at once");
            resourceName = docInfoQueryResources.get(pageIds);
            if (resourceName == null)
              throw new IOException("Could not find a matching resource for the user|comment|content|timestamp parameters; pageids = '"+pageIds+"'");
          }
          else
            throw new IOException("rvprop parameter missing or incorrect: "+rvprop);
        }
        else if (prop.equals("info"))
        {
          String inprop = req.getParameter("inprop");
          if (inprop == null || !inprop.equals("url"))
            throw new IOException("inprop parameter missing or incorrect: "+inprop);
          // url query
          if (pageIds == null)
            throw new IOException("missing pageids parameter, required for url query");
          // Sort the pageIds
          pageIds = sortStuff(pageIds);
          resourceName = urlQueryResources.get(pageIds);
          if (resourceName == null)
            throw new IOException("Could not find a matching resource for the info url parameters; pageids = '"+pageIds+"'");
        }
        else
          throw new IOException("Invalid value for prop parameter: "+prop);
      }
      else if (list != null)
      {
	if (prop != null || siprop != null)
	  throw new IOException("Cannot have both list and prop or siprop");
        if (!list.equals("allpages"))
          throw new IOException("List parameter incorrect: "+list);
        String apfrom = req.getParameter("apfrom");
        if (apfrom == null)
          apfrom = "";
        String aplimit = req.getParameter("aplimit");
        // Only two legal values for aplimit here: 1 and 500.
        if (aplimit.equals("1"))
          resourceName = checkResources.get(apfrom);
        else if (aplimit.equals("500"))
          resourceName = listResources.get(apfrom);
        else
          throw new IOException("aplimit parameter incorrect: "+aplimit);
        if (resourceName == null)
          throw new IOException("Could not find a matching resource for the list parameters; apfrom = '"+apfrom+"'");

      }
      else if (siprop != null)
      {
	if (prop != null || list != null)
	  throw new IOException("Cannot have both siprop and list or prop");
	String meta = req.getParameter("meta");
	if (meta == null || !meta.equals("siteinfo"))
	  throw new IOException("meta parameter missing or incorrect");
	resourceName = namespaceResource;
      }

      // Select the resource
      if (resourceName == null)
        throw new IOException("Could not find a matching resource for the parameters");
      
      res.setStatus(HttpServletResponse.SC_OK);
      
      OutputStream os = res.getOutputStream();
      try
      {
        InputStream is = theResourceClass.getResourceAsStream(resourceName);
        if (is == null)
          throw new IOException("Can't locate resource '"+resourceName+"' in class '"+theResourceClass.getName()+"'");
        try
        {
          byte[] bytes = new byte[65536];
          while (true)
          {
            int amt = is.read(bytes,0,bytes.length);
            if (amt == -1)
              break;
            os.write(bytes,0,amt);
          }
        }
        finally
        {
          is.close();
        }
      }
      finally
      {
        os.close();
      }
    }
  }
}
