// ***************************************************************************************************************************
// * 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.juneau.doc.internal;

import static org.apache.juneau.doc.internal.Console.*;

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

/**
 * Javadoc link checker.
 *
 * <p>
 * Runs against the generated javadocs folder looking for any broken internal links (missing files, invalid anchor tags, etc...).
 */
public class DocLinkTester {

	private static Map<String,Set<String>> ANCHORS = new LinkedHashMap<>();
	private static Pattern p = Pattern.compile("(href|src)\\=['\\\"]([^'\\\"]+)['\\\"]");
	private static Pattern p2 = Pattern.compile("(name|id)\\=['\\\"]([^'\\\"]+)['\\\"]");
	private static int errors, files, directories, links;
	private static Map<String,File> docFiles = new HashMap<>();


	/**
	 * Entry point.
	 *
	 * @param args Not used
	 */
	public static void main(String[] args) {
		try {
			long startTime = System.currentTimeMillis();
			File root = new File("../target/site/apidocs").getCanonicalFile();
			for (File fc : new File(root, "doc-files").listFiles())
				docFiles.put(fc.getAbsolutePath(), fc);
			info("Checking links in {0}", root);
			process(root);
			for (String df : new TreeMap<>(docFiles).keySet())
				error(docFiles.get(df), "unused");
			info("Checked {0} links in {1} files in {2} directories in {3}ms", links, files, directories, System.currentTimeMillis()-startTime);
			if (errors == 0)
				info("No link errors");
			else {
				Console.error(null, errors + " errors");  // NOT DEBUG
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	private static void process(File dir) throws Exception {
		if (dir.isDirectory()) {
			for (File fc : dir.listFiles()) {
				if (fc.isFile() && fc.getName().endsWith(".html")) {
					files++;
					resolveLinks(fc);
				}
			}
			for (File fc : dir.listFiles()) {
				if (fc.isDirectory() && ! fc.getName().equals("src-html")) {
					directories++;
					process(fc);
				}
			}
		}
	}

	private static boolean hasAnchor(File f, String anchor) throws Exception {
		String key = f.getCanonicalPath();
		if (! ANCHORS.containsKey(key)) {
			Set<String> s = new HashSet<>();
			String c2 = IOUtils.read(f);
			Matcher m2 = p2.matcher(c2);
			while (m2.find()) {
				s.add(m2.group(2));
			}
			ANCHORS.put(key, s);
		}
		return ANCHORS.get(key).contains(anchor);
	}

	private static void resolveLinks(File f) throws Exception {
		String contents = IOUtils.read(f);
		Matcher m = p.matcher(contents);
		while (m.find()) {
			String link = m.group(2);
			String anchor = null;
			if (link.startsWith("https://") || link.startsWith("http://") || link.startsWith("mailto:") || link.startsWith("javascript:") || link.startsWith("$") || link.startsWith("{OVERVIEW_URL}"))
				continue;
			links++;
			if (link.indexOf('?') != -1)
				link = link.substring(0, link.indexOf('?'));

			if (link.indexOf('#') != -1) {
				anchor = link.substring(link.lastIndexOf('#')+1);
				link = link.substring(0, link.lastIndexOf('#'));
			}
			File f2 = link.isEmpty() ? f : new File(f.getParentFile().getAbsolutePath() + "/" + link);
			if (! f2.exists()) {
				error(f, "missingLink=["+link+"]");
			} else if (anchor != null) {
				if (f2.isFile()) {
					boolean foundAnchor = hasAnchor(f2, anchor);
					if (! foundAnchor)
						error(f, "missingAnchor=["+link+"#"+anchor+"]");
				} else {
					error(f, "invalidAnchor=["+link+"#"+anchor+"]");
				}
			} else {
				docFiles.remove(f2.getAbsolutePath());
			}
		}
	}

	private static void error(File f, String msg) {
		errors++;
		Console.error(null, "{0}: {1}", f.getAbsolutePath(), msg);
	}
}
