package skate

import (
	"encoding/json"
	"fmt"
	"reflect"
	"testing"

	"github.com/nsf/jsondiff"
)

// XXX: Work on JSON directly, as structs can get unwieldy.
func TestOpenLibraryToRelease(t *testing.T) {
	var cases = []struct {
		work    OpenLibrarySolrDoc
		release Release
		err     error
	}{
		{
			work: OpenLibrarySolrDoc{},
			release: Release{
				ExtIDs: struct {
					Arxiv       string   `json:"arxiv,omitempty"`
					Core        string   `json:"core,omitempty"`
					DOI         string   `json:"doi,omitempty"`
					ISBN        []string `json:"isbn,omitempty"`
					Jstor       string   `json:"jstor,omitempty"`
					OLID        string   `json:"olid,omitempty"`
					PMCID       string   `json:"pmcid,omitempty"`
					PMID        string   `json:"pmid,omitempty"`
					WikidataQID string   `json:"wikidata_qid,omitempty"`
				}{},
			},
			err: nil,
		},
		{
			work: OpenLibrarySolrDoc{
				Title: "Hello World",
				Isbn: []string{
					"2844273386",
					"9782844273383",
				},
			},
			release: Release{
				ExtIDs: struct {
					Arxiv       string   `json:"arxiv,omitempty"`
					Core        string   `json:"core,omitempty"`
					DOI         string   `json:"doi,omitempty"`
					ISBN        []string `json:"isbn,omitempty"`
					Jstor       string   `json:"jstor,omitempty"`
					OLID        string   `json:"olid,omitempty"`
					PMCID       string   `json:"pmcid,omitempty"`
					PMID        string   `json:"pmid,omitempty"`
					WikidataQID string   `json:"wikidata_qid,omitempty"`
				}{
					ISBN: []string{"9782844273383"},
				},
				Title: "Hello World",
			},
			err: nil,
		},
		{
			work: OpenLibrarySolrDoc{
				Key:   "/works/OL10000003W",
				Title: "Hello World",
				Isbn: []string{
					"2844273386",
					"9782844273383",
				},
				HasFulltext: false,
			},
			release: Release{
				ExtIDs: struct {
					Arxiv       string   `json:"arxiv,omitempty"`
					Core        string   `json:"core,omitempty"`
					DOI         string   `json:"doi,omitempty"`
					ISBN        []string `json:"isbn,omitempty"`
					Jstor       string   `json:"jstor,omitempty"`
					OLID        string   `json:"olid,omitempty"`
					PMCID       string   `json:"pmcid,omitempty"`
					PMID        string   `json:"pmid,omitempty"`
					WikidataQID string   `json:"wikidata_qid,omitempty"`
				}{
					ISBN: []string{"9782844273383"},
					OLID: "OL10000003W",
				},
				Title: "Hello World",
				Extra: struct {
					ContainerName string      `json:"container_name,omitempty"`
					SubtitleValue interface{} `json:"subtitle,omitempty"` // []str or str
					Crossref      struct {
						Type string `json:"type,omitempty"`
					} `json:"crossref,omitempty"`
					DataCite struct {
						MetadataVersion int                `json:"metadataVersion,omitempty"`
						Relations       []DataCiteRelation `json:"relations,omitempty"`
					} `json:"datacite,omitempty"`
					Skate struct {
						// Mark as converted (e.g. by setting status to "ref")
						Status string `json:"status,omitempty"`
						// Carry the ref index and key around.
						Ref struct {
							Index   int64  `json:"index,omitempty"`
							Key     string `json:"key,omitempty"`
							Locator string `json:"locator,omitempty"`
							Source  string `json:"source,omitempty"`
						} `json:"ref,omitempty"`
						ResearchGate struct {
							URL string `json:"url,omitempty"`
						} `json:"rg,omitempty"`
						ResolvedContainerName string `json:"resolved_container_name"`
					} `json:"skate,omitempty"`
					OpenLibrary struct {
						HasFulltext   bool     `json:"has_fulltext,omitempty"`
						WorkID        string   `json:"work,omitempty"`
						SourceRecords []string `json:"source_records,omitempty"`
					} `json:"ol,omitempty"`
				}{
					OpenLibrary: struct {
						HasFulltext   bool     `json:"has_fulltext,omitempty"`
						WorkID        string   `json:"work,omitempty"`
						SourceRecords []string `json:"source_records,omitempty"`
					}{
						HasFulltext: false,
					},
				},
			},
			err: nil,
		},
	}
	for _, c := range cases {
		r, err := OpenLibrarySolrDocToRelease(&c.work)
		if err != nil {
			t.Fatalf("got %v, want %v", err, c.err)
		}
		if !reflect.DeepEqual(r, &c.release) {
			t.Fatalf(prettyStructDiff(r, &c.release))
		}
	}
}

func prettyStructDiff(s, t interface{}) string {
	b, err := json.MarshalIndent(s, "", "    ")
	if err != nil {
		return fmt.Sprintf("diff failed: %v", err)
	}
	c, err := json.MarshalIndent(t, "", "    ")
	if err != nil {
		return fmt.Sprintf("diff failed: %v", err)
	}
	opts := jsondiff.DefaultConsoleOptions()
	_, d := jsondiff.Compare(b, c, &opts)
	return d
}

func TestParseIsbn(t *testing.T) {
	var cases = []struct {
		s      string
		result []string
	}{
		{s: "", result: nil},
		{s: "0000000000000000000000", result: nil},
		{s: "978953510472X", result: nil},
		{s: "978-0-262-06197-1", result: []string{"9780262061971"}},
		{s: "0-262-06197-X", result: []string{"9780262061971"}},
		{s: "9789535104728 9789535104728 9789535104728", result: []string{"9789535104728"}},
		{
			s:      "Continuous, .. Dr. Marina Pana (Ed.), ISBN: 978-953-51-0472-8, InTech, Available from",
			result: []string{"9789535104728"},
		},
		{
			s:      "(IGN). Madrid. M. de Fomento ISBN 84-498-0665-8",
			result: []string{"9788449806650"},
		},
		{
			s:      "House Pvt. Limited., (2006), ISBN 9788183561426. Date accessed: August 2015.",
			result: []string{"9788183561426"},
		},
		{
			s:      "Electrolytes. 2003. ISBN 0-9726720-0-1. Pages 341, 357.",
			result: []string{"9780972672009"},
		},
		{
			s:      "£25.00. ISBN 0 631 15254 7. First published in Italian in 1985.",
			result: []string{"9780631152545"},
		},
		{
			s:      "Kluwer Academic Inc., 1996. ISBN O- 7923-9777-0.",
			result: []string{"9780792397779"},
		},
		{
			s:      "Fisheries and Aquaculture, Rome 2012; ISBN: 978-92-5- 107225-7.",
			result: []string{"9789251072257"},
		},
		{
			s:      " Praha; iSBn 80-247- 1046-3.",
			result: []string{"9788024710464"},
		},
		{
			s:      "Avialable at: http://www.urn.fi/urn:isbn:9514257693 (accessed: 3.07.2017).",
			result: []string{"9789514257698"},
		},
		{
			s:      "Colegio de Arquitectos de Málaga, 1987, ISBN: 8439899939, 9788439899938.",
			result: []string{"9788439899938"},
		},
	}
	for _, c := range cases {
		r := ParseIsbn(c.s)
		if !reflect.DeepEqual(r, c.result) {
			t.Fatalf("got %v, want %v", r, c.result)
		}
	}
}

func TestLinkHash(t *testing.T) {
	var cases = []struct {
		bref     BiblioRef
		linkHash string
	}{
		{
			bref:     BiblioRef{},
			linkHash: "7cae9fc61f167bc26cc3839f15457fe87b2be4e1",
		},
		{
			bref:     BiblioRef{SourceReleaseIdent: "123"},
			linkHash: "a0969f96c14cb42d298117e1927bd409873173a2",
		},
		{
			bref: BiblioRef{
				SourceReleaseIdent: "123",
				TargetReleaseIdent: "456",
			},
			linkHash: "fc:123--fc:456",
		},
		{
			bref: BiblioRef{
				SourceReleaseIdent:    "123",
				TargetOpenLibraryWork: "/works/OL456M",
			},
			linkHash: "fc:123--ol:/works/OL456M",
		},
		{
			bref: BiblioRef{
				SourceReleaseIdent: "123",
				TargetURL:          "http://fatcat.wiki",
			},
			linkHash: "fc:123--wb:http://fatcat.wiki",
		},
	}
	for _, c := range cases {
		result := c.bref.LinkHash()
		if result != c.linkHash {
			t.Fatalf("got %v, want %v", result, c.linkHash)
		}
	}
}

func TestReleaseToUnstructured(t *testing.T) {
	var cases = []struct {
		r *Release
		s string
	}{
		{r: &Release{}, s: ""},
		{r: &Release{Volume: "12"}, s: "vol. 12"},
		{r: &Release{Volume: "12", Issue: "X"}, s: "vol. 12, no. X"},
		{r: &Release{Volume: "12", Issue: "X", ReleaseYearValue: "1999"}, s: "vol. 12, no. X, 1999"},
		{r: &Release{Volume: "12", ContainerName: "Solar",
			ReleaseYearValue: "1999"}, s: "Solar, vol. 12, 1999"},
		{r: &Release{ExtIDs: struct {
			Arxiv       string   `json:"arxiv,omitempty"`
			Core        string   `json:"core,omitempty"`
			DOI         string   `json:"doi,omitempty"`
			ISBN        []string `json:"isbn,omitempty"` // should be isbn13
			Jstor       string   `json:"jstor,omitempty"`
			OLID        string   `json:"olid,omitempty"`
			PMCID       string   `json:"pmcid,omitempty"`
			PMID        string   `json:"pmid,omitempty"`
			WikidataQID string   `json:"wikidata_qid,omitempty"`
		}{
			DOI: "10.1234/1234",
		}, Volume: "12", ContainerName: "Solar",
			ReleaseYearValue: "1999"}, s: "Solar, vol. 12, 1999, 10.1234/1234"},
		{r: &Release{
			Title: "ABC",
		}, s: "ABC"},
		{r: &Release{
			Title:            "ABC",
			ReleaseYearValue: "2000",
		}, s: "ABC, 2000"},
		{r: &Release{
			ContainerName: "Journal of Letters",
		}, s: "Journal of Letters"},
		{r: &Release{
			ContainerName: "Journal of Letters",
		}, s: "Journal of Letters"},
		{r: &Release{
			Title:         "ABC",
			ContainerName: "Journal of Letters",
		}, s: "ABC. Journal of Letters"},
		{r: &Release{
			Title:         "ABC",
			ContainerName: "Journal of Letters",
			Volume:        "12",
			Pages:         "1-10",
		}, s: "ABC. Journal of Letters, vol. 12, pp. 1-10"},
		{r: &Release{
			Title:            "ABC",
			ReleaseYearValue: "2010",
			ContainerName:    "Journal of Letters",
		}, s: "ABC. Journal of Letters, 2010"},
		{r: &Release{
			Title:         "ABC",
			ContainerName: "Journal of Letters",
			Contribs: []struct {
				Index   int    `json:"index,omitempty"`
				RawName string `json:"raw_name,omitempty"`
				Role    string `json:"role,omitempty"`
			}{
				{
					RawName: "Liam Ling",
				},
			},
		}, s: "Liam Ling. ABC. Journal of Letters"},
		{r: &Release{
			Title:         "ABC",
			ContainerName: "Journal of Letters",
			Contribs: []struct {
				Index   int    `json:"index,omitempty"`
				RawName string `json:"raw_name,omitempty"`
				Role    string `json:"role,omitempty"`
			}{
				{
					RawName: "Liam Ling",
				},
				{
					RawName: "Lin Lee",
				},
			},
		}, s: "Liam Ling, Lin Lee. ABC. Journal of Letters"},
	}
	for _, c := range cases {
		got := ReleaseToUnstructured(c.r)
		if got != c.s {
			t.Fatalf("got %v, want %v", got, c.s)
		}
	}
}

func BenchmarkParseIsbn(b *testing.B) {
	for n := 0; n < b.N; n++ {
		ParseIsbn("House Pvt. Limited., (2006), ISBN 9788183561426. Date accessed: August 2015.")
	}
}

func BenchmarkRefToRelease(b *testing.B) {
	var ref Ref
	_ = json.Unmarshal([]byte(`
    {
      "biblio": {
        "arxiv_id": "123",
        "container_name": "IEEE Trans. Pattern Anal. Mach. Intell",
        "contrib_raw_names": [
          "M Ben-Ezra",
          "S K Nayar"
        ],
        "doi": "123",
        "issue": "6",
        "pages": "689-698",
        "pmcid": "123",
        "publisher": "ABC",
        "title": "Motion-based motion deblurring",
        "unstructured": "M. Ben-Ezra and S. K. Nayar. Motion-based motion deblurring. IEEE Trans. Pattern Anal. Mach. Intell., 26(6):689-698, 2004. 2",
        "url": "https://abc.com",
        "volume": "26",
        "year": 2004
      },
      "index": 0,
      "key": "b0",
      "ref_source": "grobid",
      "release_ident": "26qgat7mzrerjacrlsz3gdmcgy",
      "release_year": 2014,
      "work_ident": "aaaoe2wcbvdjthnv36dlqgkray"
    }`), &ref)
	for n := 0; n < b.N; n++ {
		_, _ = RefToRelease(&ref)
	}
}