package parser

import (
	"slices"

	"github.com/creachadair/mds/mapset"
	"github.com/publicsuffix/list/tools/internal/domain"
)

// Exceptions are parts of the PSL that would fail current validation
// and stylistic requirements, but are exempted due to predating those
// rules.
//
// See the bottom of this file for the exceptions themselves.

// exemptFromContactInfo reports whether the block owned by entity is
// exempt from the requirement to have a contact email address.
func exemptFromContactInfo(entity string) bool {
	return slices.Contains(missingEmail, entity)
}

// exemptFromSorting reports whether the block owned by entity is
// exempt from the sorting requirement that normally applies in the
// private domains section.
func exemptFromSorting(entity string) bool {
	return slices.Contains(incorrectSort, entity)
}

// exemptFromTXT reports whether the given domain name is exempt from
// the requirement to have a _psl TXT record.
func exemptFromTXT(domain domain.Name) bool {
	if missingTXT.Has(domain.String()) {
		return true
	}
	return false
}

// adjustTXTPR returns a PR number to use instead of prNum for
// checking, or prNum unchanged if no adjustment is needed.
func adjustTXTPR(prNum int) int {
	if ret, ok := txtReplacePRs[prNum]; ok {
		return ret
	}
	return prNum
}

// acceptPRForDomain reports whether the given prNum should be
// accepted as verification for the given domain, without further
// checking of the Github PR.
func acceptPRForDomain(domain domain.Name, prNum int) bool {
	if exc, ok := txtAcceptPRs[domain.String()]; ok && exc == prNum {
		return true
	}
	return false
}

// missingEmail are source code blocks in the private domains section
// that are allowed to lack email contact information.
var missingEmail = []string{
	"611 blockchain domain name system",
	"co.ca",
	"DynDNS.com",
	"Hashbang",
	"HostyHosting",
	"info.at",
	".KRD",
	"Michau Enterprises Limited",
	"Nicolaus Copernicus University in Torun - MSK TORMAN",
	"TASK geographical domains",
	"CoDNS B.V.",
	".pl domains (grandfathered)",
	"QA2",
}

// incorrectSort are entities in the private domains section that are
// allowed to be in the wrong sort order.
var incorrectSort = []string{}

// missingTXT are the domains that are exempt from the _psl TXT record
// requirement.
var missingTXT = mapset.New(
	"001www.com",
	"0emm.com",
	"4u.com",
	"accesscam.org",
	"ac.ru",
	"adimo.co.uk",
	"ae.org",
	"africa.com",
	"akadns.net",
	"akamaiedge.net",
	"akamaiedge-staging.net",
	"akamaihd.net",
	"akamaihd-staging.net",
	"akamai.net",
	"akamaiorigin.net",
	"akamaiorigin-staging.net",
	"akamai-staging.net",
	"akamaized.net",
	"akamaized-staging.net",
	"al.eu.org",
	"api.stdlib.com",
	"ap.ngrok.io",
	"app.render.com",
	"appspot.com",
	"art.pl",
	"asso.eu.org",
	"at-band-camp.net",
	"at.eu.org",
	"ath.cx",
	"au.eu.org",
	"au.ngrok.io",
	"aus.basketball",
	"azure-api.net",
	"azureedge.net",
	"azurefd.net",
	"azure-mobile.net",
	"azurewebsites.net",
	"barrell-of-knowledge.info",
	"barrel-of-knowledge.info",
	"beagleboard.io",
	"be.eu.org",
	"better-than.tv",
	"bg.eu.org",
	"biz.at",
	"biz.ua",
	"blob.core.windows.net",
	"blogdns.com",
	"blogdns.net",
	"blogdns.org",
	"blogsite.org",
	"blogspot.ae",
	"blogspot.al",
	"blogspot.am",
	"blogspot.ba",
	"blogspot.be",
	"blogspot.bg",
	"blogspot.bj",
	"blogspot.ca",
	"blogspot.cf",
	"blogspot.ch",
	"blogspot.cl",
	"blogspot.co.at",
	"blogspot.co.id",
	"blogspot.co.il",
	"blogspot.co.ke",
	"blogspot.com",
	"blogspot.com.ar",
	"blogspot.com.au",
	"blogspot.com.br",
	"blogspot.com.by",
	"blogspot.com.co",
	"blogspot.com.cy",
	"blogspot.com.ee",
	"blogspot.com.eg",
	"blogspot.com.es",
	"blogspot.com.mt",
	"blogspot.com.ng",
	"blogspot.com.tr",
	"blogspot.com.uy",
	"blogspot.co.nz",
	"blogspot.co.uk",
	"blogspot.co.za",
	"blogspot.cv",
	"blogspot.cz",
	"blogspot.de",
	"blogspot.dk",
	"blogspot.fi",
	"blogspot.fr",
	"blogspot.gr",
	"blogspot.hk",
	"blogspot.hr",
	"blogspot.hu",
	"blogspot.ie",
	"blogspot.in",
	"blogspot.is",
	"blogspot.it",
	"blogspot.jp",
	"blogspot.kr",
	"blogspot.li",
	"blogspot.lt",
	"blogspot.lu",
	"blogspot.md",
	"blogspot.mk",
	"blogspot.mx",
	"blogspot.my",
	"blogspot.nl",
	"blogspot.no",
	"blogspot.pe",
	"blogspot.pt",
	"blogspot.qa",
	"blogspot.re",
	"blogspot.ro",
	"blogspot.rs",
	"blogspot.ru",
	"blogspot.se",
	"blogspot.sg",
	"blogspot.si",
	"blogspot.sk",
	"blogspot.sn",
	"blogspot.tw",
	"blogspot.ug",
	"blogspot.vn",
	"boldlygoingnowhere.org",
	"bookonline.app",
	"br.com",
	"broke-it.net",
	"buyshouses.net",
	"ca.eu.org",
	"camdvr.org",
	"casacam.net",
	"cd.eu.org",
	"cechire.com",
	"ch.eu.org",
	"cloudapp.net",
	"cloudfront.net",
	"cloudfunctions.net",
	"cloudns.biz",
	"cloudns.in",
	"cloudns.info",
	"cloudns.us",
	"cn.com",
	"cn.eu.org",
	"co.business",
	"co.ca",
	"co.com",
	"co.cz",
	"codeberg.page",
	"codespot.com",
	"co.education",
	"co.events",
	"co.financial",
	"co.krd",
	"com.de",
	"com.se",
	"co.network",
	"co.nl",
	"co.no",
	"co.pl",
	"co.place",
	"co.technology",
	"co.ua",
	"cryptonomic.net",
	"csx.cc",
	"curv.dev",
	"cy.eu.org",
	"cyon.link",
	"cyon.site",
	"cz.eu.org",
	"ddnsfree.com",
	"ddnsgeek.com",
	"ddnss.de",
	"de.com",
	"de.eu.org",
	"demo.datadetect.com",
	"diskstation.me",
	"dk.eu.org",
	"dnsalias.com",
	"dnsalias.net",
	"dnsalias.org",
	"dnsdojo.com",
	"dnsdojo.net",
	"dnsdojo.org",
	"dnshome.de",
	"does-it.net",
	"doesntexist.com",
	"doesntexist.org",
	"dontexist.com",
	"dontexist.net",
	"dontexist.org",
	"doomdns.com",
	"doomdns.org",
	"dreamhosters.com",
	"drud.us",
	"dscloud.biz",
	"dscloud.me",
	"dscloud.mobi",
	"dsmynas.com",
	"dsmynas.net",
	"dsmynas.org",
	"duckdns.org",
	"dvrdns.org",
	"dynalias.com",
	"dynalias.net",
	"dynalias.org",
	"dynathome.net",
	"dyndns-at-home.com",
	"dyndns-at-work.com",
	"dyndns.biz",
	"dyndns-blog.com",
	"dyndns.dappnode.io",
	"dyndns-free.com",
	"dyndns-home.com",
	"dyndns.info",
	"dyndns-ip.com",
	"dyndns-mail.com",
	"dyndns-office.com",
	"dyndns.org",
	"dyndns-pics.com",
	"dyndns-remote.com",
	"dyndns-server.com",
	"dyndns.tv",
	"dyndns-web.com",
	"dyndns-wiki.com",
	"dyndns-work.com",
	"dyndns.ws",
	"dyn-o-saur.com",
	"dynu.net",
	"dynv6.net",
	"e4.cz",
	"edgekey.net",
	"edgekey-staging.net",
	"edgesuite.net",
	"edgesuite-staging.net",
	"edu.eu.org",
	"edu.krd",
	"edu.ru",
	"ee.eu.org",
	"endofinternet.net",
	"endofinternet.org",
	"endoftheinternet.org",
	"es.eu.org",
	"est-a-la-maison.com",
	"est-a-la-masion.com",
	"est-le-patron.com",
	"est-mon-blogueur.com",
	"eu.com",
	"eu.ngrok.io",
	"eu.org",
	"familyds.com",
	"familyds.net",
	"familyds.org",
	"fi.eu.org",
	"firebaseapp.com",
	"fly.dev",
	"for-better.biz",
	"forgot.her.name",
	"forgot.his.name",
	"for-more.biz",
	"for-our.info",
	"for-some.biz",
	"for-the.biz",
	"freeddns.org",
	"free.hr",
	"fr.eu.org",
	"from-ak.com",
	"from-al.com",
	"from-ar.com",
	"from-az.net",
	"from-ca.com",
	"from-co.net",
	"from-ct.com",
	"from-dc.com",
	"from-de.com",
	"from-fl.com",
	"from-ga.com",
	"from-hi.com",
	"from-ia.com",
	"from-id.com",
	"from-il.com",
	"from-in.com",
	"from-ks.com",
	"from-ky.com",
	"from-la.net",
	"from-ma.com",
	"from-md.com",
	"from-me.org",
	"from-mi.com",
	"from-mn.com",
	"from-mo.com",
	"from-ms.com",
	"from-mt.com",
	"from-nc.com",
	"from-nd.com",
	"from-ne.com",
	"from-nh.com",
	"from-nj.com",
	"from-nm.com",
	"from-nv.com",
	"from-ny.net",
	"from-oh.com",
	"from-ok.com",
	"from-or.com",
	"from-pa.com",
	"from-pr.com",
	"from-ri.com",
	"from-sc.com",
	"from-sd.com",
	"from-tn.com",
	"from-tx.com",
	"from-ut.com",
	"from-va.com",
	"from-vt.com",
	"from-wa.com",
	"from-wi.com",
	"from-wv.com",
	"from-wy.com",
	"ftpaccess.cc",
	"fuettertdasnetz.de",
	"game-host.org",
	"game-server.cc",
	"gb.net",
	"gdansk.pl",
	"gda.pl",
	"gdynia.pl",
	"getmyip.com",
	"gets-it.net",
	"giize.com",
	"github.io",
	"githubusercontent.com",
	"gleeze.com",
	"gliwice.pl",
	"go.dyndns.org",
	"googleapis.com",
	"googlecode.com",
	"gotdns.com",
	"gotdns.org",
	"gotpantheon.com",
	"gov.ru",
	"gr.com",
	"gr.eu.org",
	"groks-the.info",
	"groks-this.info",
	"gsj.bz",
	"günstigbestellen.de",
	"günstigliefern.de",
	"ham-radio-op.net",
	"hashbang.sh",
	"hepforge.org",
	"here-for-more.info",
	"herokuapp.com",
	"hk.com",
	"hk.org",
	"hobby-site.com",
	"hobby-site.org",
	"homedns.org",
	"home.dyndns.org",
	"homeftp.net",
	"homeftp.org",
	"homeip.net",
	"homelinux.com",
	"homelinux.net",
	"homelinux.org",
	"homeunix.com",
	"homeunix.net",
	"homeunix.org",
	"hosting.ovh.net",
	"hr.eu.org",
	"hu.eu.org",
	"hu.net",
	"hzc.io",
	"i234.me",
	"iamallama.com",
	"id.firewalledreplit.co",
	"id.forgerock.io",
	"ie.eu.org",
	"iki.fi",
	"il.eu.org",
	"inc.hk",
	"ind.mom",
	"in.eu.org",
	"info.at",
	"in.net",
	"in.ngrok.io",
	"int.eu.org",
	"in-the-band.net",
	"int.ru",
	"is-a-anarchist.com",
	"is-a-blogger.com",
	"is-a-bookkeeper.com",
	"is-a-bruinsfan.org",
	"is-a-bulls-fan.com",
	"is-a-candidate.org",
	"is-a-caterer.com",
	"is-a-celticsfan.org",
	"is-a-chef.com",
	"is-a-chef.net",
	"is-a-chef.org",
	"is-a-conservative.com",
	"is-a-cpa.com",
	"is-a-cubicle-slave.com",
	"is-a-democrat.com",
	"is-a-designer.com",
	"is-a-doctor.com",
	"is-a-financialadvisor.com",
	"is-a-geek.com",
	"isa-geek.com",
	"is-a-geek.net",
	"isa-geek.net",
	"is-a-geek.org",
	"isa-geek.org",
	"is-a-green.com",
	"is-a-guru.com",
	"is-a-hard-worker.com",
	"isa-hockeynut.com",
	"is-a-hunter.com",
	"is-a-knight.org",
	"is-a-landscaper.com",
	"is-a-lawyer.com",
	"is-a-liberal.com",
	"is-a-libertarian.com",
	"is-a-linux-user.org",
	"is-a-llama.com",
	"is-a-musician.com",
	"is-an-accountant.com",
	"is-an-actor.com",
	"is-an-actress.com",
	"is-an-anarchist.com",
	"is-an-artist.com",
	"is-a-nascarfan.com",
	"is-an-engineer.com",
	"is-an-entertainer.com",
	"is-a-nurse.com",
	"is-a-painter.com",
	"is-a-patsfan.org",
	"is-a-personaltrainer.com",
	"is-a-photographer.com",
	"is-a-player.com",
	"is-a-republican.com",
	"is-a-rockstar.com",
	"is-a-socialist.com",
	"is-a-soxfan.org",
	"is-a-student.com",
	"is-a-teacher.com",
	"is-a-techie.com",
	"is-a-therapist.com",
	"is-by.us",
	"is-certified.com",
	"is.eu.org",
	"is-found.org",
	"is-gone.com",
	"is-into-anime.com",
	"is-into-cars.com",
	"is-into-cartoons.com",
	"is-into-games.com",
	"is-leet.com",
	"is-lost.org",
	"is-not-certified.com",
	"is-saved.org",
	"is-slick.com",
	"issmarterthanyou.com",
	"isteingeek.de",
	"istmein.de",
	"is-uberleet.com",
	"is-very-bad.org",
	"is-very-evil.org",
	"is-very-good.org",
	"is-very-nice.org",
	"is-very-sweet.org",
	"is-with-theband.com",
	"it.eu.org",
	"jp.eu.org",
	"jpn.com",
	"jp.net",
	"jp.ngrok.io",
	"js.wpenginepowered.com",
	"keymachine.de",
	"kicks-ass.net",
	"kicks-ass.org",
	"knowsitall.info",
	"kozow.com",
	"krakow.pl",
	"kr.eu.org",
	"land-4-sale.us",
	"lebtimnetz.de",
	"leitungsen.de",
	"lib.de.us",
	"likescandy.com",
	"likes-pie.com",
	"lk3.ru",
	"loseyourip.com",
	"ltd.hk",
	"lt.eu.org",
	"lu.eu.org",
	"lv.eu.org",
	"mazeplay.com",
	"med.pl",
	"me.eu.org",
	"memset.net",
	"merseine.nu",
	"mex.com",
	"mil.ru",
	"mine.nu",
	"miniserver.com",
	"misconfused.org",
	"mk.eu.org",
	"moonscale.net",
	"mt.eu.org",
	"myasustor.com",
	"mydatto.com",
	"myddns.rocks",
	"mydrobo.com",
	"myds.me",
	"my.eu.org",
	"mypep.link",
	"mypets.ws",
	"myphotos.cc",
	"mywire.org",
	"neat-url.com",
	"net.eu.org",
	"netfy.app",
	"nfshost.com",
	"ng.eu.org",
	"nl.eu.org",
	"no.eu.org",
	"now-dns.top",
	"nz.basketball",
	"nz.eu.org",
	"office-on-the.net",
	"onfabrica.com",
	"on-rancher.cloud",
	"onrender.com",
	"on-rio.io",
	"on-the-web.tv",
	"ooguy.com",
	"operaunite.com",
	"outsystemscloud.com",
	"ownprovider.com",
	"ox.rs",
	"pagespeedmobilizer.com",
	"platter-app.com",
	"pl.eu.org",
	"podzone.net",
	"podzone.org",
	"poznan.pl",
	"pp.ua",
	"protonet.io",
	"pt.eu.org",
	"qa2.com",
	"qbuser.com",
	"rackmaze.com",
	"rackmaze.net",
	"readmyblog.org",
	"realm.cz",
	"repl.run",
	"rhcloud.com",
	"ric.jelastic.vps-host.net",
	"ro.eu.org",
	"routingthecloud.net",
	"ru.com",
	"ru.eu.org",
	"sa.com",
	"sandcats.io",
	"sa.ngrok.io",
	"saves-the-whales.com",
	"scrapper-site.net",
	"scrapping.cc",
	"sdscloud.pl",
	"secaas.hk",
	"se.eu.org",
	"selfip.biz",
	"selfip.com",
	"selfip.info",
	"selfip.net",
	"selfip.org",
	"sells-for-less.com",
	"sells-for-u.com",
	"sells-it.net",
	"sellsyourhome.org",
	"se.net",
	"senseering.net",
	"servebbs.com",
	"servebbs.net",
	"servebbs.org",
	"serveftp.net",
	"serveftp.org",
	"servegame.org",
	"servicebus.windows.net",
	"service.gov.uk",
	"shacknet.nu",
	"shoparena.pl",
	"shopware.store",
	"si.eu.org",
	"simple-url.com",
	"sk.eu.org",
	"sn.mynetname.net",
	"sopot.pl",
	"spacekit.io",
	"space-to-rent.com",
	"static.observableusercontent.com",
	"stg.dev",
	"stuff-4-sale.org",
	"stuff-4-sale.us",
	"synology.me",
	"teaches-yoga.com",
	"theworkpc.com",
	"thruhere.net",
	"tickets.io",
	"toolforge.org",
	"townnews-staging.com",
	"traeumtgerade.de",
	"trafficmanager.net",
	"translate.goog",
	"tr.eu.org",
	"uk.com",
	"uk.eu.org",
	"uk.net",
	"us.com",
	"user.party.eus",
	"us.eu.org",
	"us.ngrok.io",
	"us.org",
	"vercel.app",
	"vercel.dev",
	"virtual-user.de",
	"vpnplus.to",
	"webhop.biz",
	"webhop.info",
	"webhop.net",
	"webhop.org",
	"webpaas.ovh.net",
	"webredirect.org",
	"withgoogle.com",
	"withyoutube.com",
	"wmcloud.org",
	"wmflabs.org",
	"worse-than.tv",
	"wpenginepowered.com",
	"wpmucdn.com",
	"writesthisblog.com",
	"wroc.pl",
	"xen.prgmr.com",
	"yolasite.com",
	"za.bz",
	"za.com",
	"zakopane.pl",
	"za.net",
	"za.org",
)

// txtReplacePRs substitutes some TXT PR numbers for
// replacements. This is to paper over some early PSL submissions that
// used the _psl process, where the domain owners complied with all
// PSL policies, but for various reasons their own PR was closed and a
// PSL maintainer sent and merged their own PR with the same change.
//
// While the _psl record is technically not quite right, since it
// points to a PR that was never merged, the owners of the suffixes in
// question did everything right, so it wouldn't be fair to punish
// them for the break in the chain of custody.
var txtReplacePRs = map[int]int{
	// Lukanet Ltd domains.
	596: 652,
	// CloudAccess.net
	372: 466,
	// .fr update
	1621: 1823,
	// bounty-full.com
	104: 230,
	// drayddns.com
	451: 475,
	// dy.fi
	95: 229,
	// activetrail.biz
	1541: 1655,
	// freeboxos
	129: 232,
	// rs.ba
	1289: 1367,
	// Hoplix
	1282: 1405,
	1364: 1405,
}

// txtAcceptPRs maps suffix strings to a PR number that can be
// accepted for that suffix without further checking.
//
// This is to work around situations similar to txtReplacePRs, where
// the suffix owners followed all the rules, but the change was either
// merged separately from the PR, or Github's API has bad data and
// returns bogus information that prevents us from tracing the change.
var txtAcceptPRs = map[string]int{
	// Change pushed to master by maintainer rather than merging PR.
	"js.org": 264,

	// https://github.com/publicsuffix/list/pull/174 . API returns
	// bogus merge SHA.
	"xenapponazure.com": 174,
	"biz.dk":            174,
	"co.dk":             174,
	"firm.dk":           174,
	"reg.dk":            174,
	"store.dk":          174,

	// https://github.com/publicsuffix/list/pull/155 . API returns
	// bogus merge SHA.
	"pantheonsite.io": 155,

	// https://github.com/publicsuffix/list/pull/10 . API returns
	// bogus merge SHA.
	"bmoattachments.org": 10,

	// https://github.com/publicsuffix/list/pull/15 . API returns
	// bogus merge SHA.
	"c.cdn77.org":                 15,
	"cdn77-ssl.net":               15,
	"r.cdn77.net":                 15,
	"rsc.cdn77.org":               15,
	"ssl.origin.cdn77-secure.org": 15,

	// PR sent and then closed due to unresponsiveness, but the suffix
	// owner updated their _psl record.
	"hb.cldmail.ru": 1871,

	// https://github.com/publicsuffix/list/pull/1473 shuffled around
	// some other domains owned by the same entities, but they updated
	// all their TXT records not just the changed ones.
	"ngo.ng":  1473,
	"orx.biz": 1473,
	"biz.gl":  1473,
	"ltd.ng":  1473,
	"col.ng":  1473,
	"gen.ng":  1473,

	// https://github.com/publicsuffix/list/pull/1851 proposed moving
	// priv.at to the ICANN section and set up a _psl record to prove
	// ownership, but the change was rejected. That means the PR is
	// technically invalid for this TXT record, but it does
	// demonstrate ownership by the PR's author so is useful to keep
	// around as a "valid" entry with an exception.
	"priv.at": 1851,
}
