{# Copyright 2020 Curtin University
#
# Licensed 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.

# Author: Richard Hosking, Keegan Smith #}

-- Convert 10-digit ISBNs to 13-digit
CREATE TEMPORARY FUNCTION normalise_isbn_string(input STRING)
RETURNS STRING
  LANGUAGE js AS r"""

  function calc_isbn13_check_digit(isbn13) {
    var mask = [1,3,1,3,1,3,1,3,1,3,1,3];

    var prefix = [];
    for(let i = 0; i < 12; i++) {
        prefix.push(Number(isbn13[i]));
    }

    let check_digit = 0;
    for(let i = 0; i < 12; i++) {
        check_digit += mask[i]*prefix[i];
    }

    return (10-(check_digit % 10)) % 10;
  }

  function calc_isbn10_check_digit(isbn10) {
    var mask = [10,9,8,7,6,5,4,3,2];

    var prefix = [];
    for(let i = 0; i < 9; i++) {
        prefix.push(Number(isbn10[i]));
    }

    let check_digit = 0;
    for(let i = 0; i < 9; i++) {
        check_digit += mask[i]*prefix[i];
    }

    check_digit = (11-(check_digit % 11)) % 11;

    if(check_digit == 10)
        return 'X';
    
    return check_digit;
  }

  function is_isbn13(isbn) {
    if(isbn.length != 13) {
        return false;
    }

    if(isNaN(Number(isbn))) {
        return false;
    }

    let check_digit = String(calc_isbn13_check_digit(isbn));
    return check_digit == isbn[12];
}

  function is_isbn10(isbn) {
    if(isbn.length != 10) {
        return false;
    }

    if(isNaN(Number(isbn.slice(0,9)))) {
        return false;
    }

    let check_digit = String(calc_isbn10_check_digit(isbn));
    return check_digit == isbn[9];
}

  function convert_isbn10_to_isbn13(isbn10) {
    let isbn = "978" + isbn10.slice(0, 9);
    let check_digit = calc_isbn13_check_digit(isbn);
    isbn += String(check_digit);
    return isbn;
  }

  function strip_isbn_string(isbn) {
    var regexp = /[^0-9X]/gi;
    return isbn.replace(regexp, "");
  }

  let stripped = strip_isbn_string(input);
    
  if(stripped.length == 13 && is_isbn13(stripped)) {
      return stripped;
  }

  if(stripped.length == 10 && is_isbn10(stripped)) {
      return convert_isbn10_to_isbn13(stripped);
  }

  return null;
""";

WITH

-- Intermediate crossref events table
temp_intermediate_crossref_events as (
SELECT
  doi,
  ARRAY(SELECT as STRUCT source, SUM(count) as count FROM UNNEST(months) GROUP BY source) as events,
  months,
  ARRAY(SELECT as STRUCT CAST(SPLIT(month, "-")[SAFE_OFFSET(0)] as int64) as year, source, SUM(count) as count FROM UNNEST(months) GROUP BY year, source) as years
FROM (
SELECT
  doi,
  ARRAY_AGG(STRUCT(month, source, count)) as months
FROM (
  SELECT
    (UPPER(TRIM(SUBSTR(obj_id, 17)))) as doi,
    safe.FORMAT_TIMESTAMP('%Y-%m', occurred_at) as month,
    source_id as source,
    COUNT(id) as count
  FROM `{{ crossref_events_table_id }}`
  WHERE safe.FORMAT_TIMESTAMP('%Y-%m', occurred_at) is not null
  GROUP BY
  doi, source_id, month)
GROUP BY doi
)),


-- Temp DOI table
dois_temp_table as (
SELECT
  *
FROM
  (SELECT
    UPPER(TRIM(ref.doi)) as doi,
    STRUCT(
            title, abstract, issued.date_parts[offset(0)] as published_year,
            CASE WHEN ARRAY_LENGTH(issued.date_parts) > 1 THEN issued.date_parts[offset(1)] ELSE 13 END as published_month,
            CONCAT(issued.date_parts[offset(0)], "-", CASE WHEN ARRAY_LENGTH(issued.date_parts) > 1 THEN issued.date_parts[offset(1)] ELSE 13 END) as published_year_month,
            type, ISSN, ISBN, issn_type, publisher_location, publisher, member, prefix, container_title, short_container_title, group_title, references_count,
            is_referenced_by_count, subject, published_print, license, volume, funder, page, author, link, clinical_trial_number, alternative_id
    ) as crossref,
    (SELECT as STRUCT * from temp_intermediate_crossref_events as events WHERE events.doi = UPPER(TRIM(ref.doi))) as events
  FROM `{{ crossref_metadata_table_id }}` as ref
  WHERE ARRAY_LENGTH(issued.date_parts) > 0)),


-- Temp ISBN table
isbns as (
SELECT
    normalise_isbn_string(isbn) as isbn,
    doi,
    crossref.title,
    crossref.type,
    crossref.publisher,
    crossref.published_year,
    crossref.published_year_month,
    (SELECT ARRAY_AGG(normalise_isbn_string(candidate_isbn) IGNORE NULLS) FROM UNNEST(crossref.isbn) as candidate_isbn) as work_isbns
    FROM dois_temp_table as original, UNNEST(crossref.ISBN) as isbn),


-- Temp books
books as (
SELECT
    book.isbn,
    book.crossref_objects,
    book_part.chapters
FROM (
SELECT 
    isbn,
    ARRAY_AGG(STRUCT(doi, title, type, publisher, published_year, published_year_month, work_isbns)) as crossref_objects
 FROM isbns
 WHERE type in ("book", "monograph") AND isbn IS NOT NULL
 GROUP BY isbn) as book
 left join (
     SELECT 
        isbn,
        ARRAY_AGG(STRUCT(doi, title, type) ORDER BY doi) as chapters
    FROM isbns
    WHERE type in ("book-chapter") AND isbn IS NOT NULL
    GROUP BY isbn
 ) as book_part on book.isbn = book_part.isbn),


-- Temp aggregated events
ISBN_DOI as (SELECT 
  ISBN,
  ARRAY_CONCAT(ARRAY(SELECT doi FROM book.crossref_objects), ARRAY(SELECT doi FROM book.chapters)) as dois
FROM books as book),

events_matched as (SELECT
  ISBN,
  ARRAY_CONCAT_AGG(crossref_events.events) as events,
  ARRAY_CONCAT_AGG(crossref_events.months) as months,
  ARRAY_CONCAT_AGG(crossref_events.years) as years,
FROM ISBN_DOI, UNNEST(dois) as doi
LEFT JOIN temp_intermediate_crossref_events as crossref_events ON crossref_events.doi = doi
GROUP BY ISBN),

events_aggregated as (SELECT
  ISBN,
  ARRAY(SELECT as STRUCT source, SUM(count) as count FROM UNNEST(events) GROUP BY source) as overall,
  ARRAY(SELECT as STRUCT month, source, SUM(count) as count FROM UNNEST(months) GROUP BY month, source ORDER BY month DESC) as months,
  ARRAY(SELECT as STRUCT year, source, SUM(count) as count FROM UNNEST(years) GROUP BY year, source ORDER BY year DESC) as years
FROM events_matched)


-- Main Query
SELECT
    book.isbn,
    book.crossref_objects,
    book.chapters,
    STRUCT(
        events.overall,
        events.months,
        events.years
    ) as events
FROM books as book 
LEFT join events_aggregated as events on events.ISBN = book.ISBN