import pytest
from unittest import mock
from datetime import datetime

from books.koreader import (
    KoReaderBookColumn,
    build_book_map,
    build_page_data,
    build_scrobbles_from_book_map,
    get_author_str_from_row,
    lookup_or_create_authors_from_author_str,
    create_book_from_row,
    process_koreader_sqlite_file,
)


@pytest.mark.django_db
@mock.patch("requests.get")
def test_build_book_map(get_mock, koreader_rows, valid_response):
    get_mock.return_value = valid_response
    book_map = build_book_map(koreader_rows.BOOK_ROWS)
    assert len(book_map) == 1


@pytest.mark.django_db
@mock.patch("requests.get")
def test_load_page_data_to_map(get_mock, koreader_rows, valid_response):
    get_mock.return_value = valid_response
    book_map = build_page_data(
        koreader_rows.PAGE_STATS_ROWS,
        build_book_map(koreader_rows.BOOK_ROWS),
    )
    assert (
        len(book_map[1]["pages"])
        == koreader_rows.BOOK_ROWS[0][KoReaderBookColumn.TOTAL_READ_PAGES.value]
    )


@pytest.mark.django_db
@mock.patch("requests.get")
def test_build_scrobbles_from_pages(get_mock, koreader_rows, demo_user, valid_response):
    get_mock.return_value = valid_response
    book_map = build_book_map(koreader_rows.BOOK_ROWS)
    book_map = build_page_data(koreader_rows.PAGE_STATS_ROWS, book_map)

    scrobbles = build_scrobbles_from_book_map(book_map, demo_user)
    # The test data generator adds the session-gap 3600s AFTER the trigger page
    # (not before), so the first session includes 21 pages (1-21), and each
    # subsequent session has 20 until the last. The last page is now included
    # in the final scrobble instead of being orphaned.
    expected_scrobbles = 6 * len(book_map.keys())
    assert len(scrobbles) == expected_scrobbles
    assert len(scrobbles[0].logdata.page_data.keys()) == 21
    assert len(scrobbles[1].logdata.page_data.keys()) == 20
    assert len(scrobbles[2].logdata.page_data.keys()) == 20
    assert len(scrobbles[3].logdata.page_data.keys()) == 20
    assert len(scrobbles[4].logdata.page_data.keys()) == 20
    assert len(scrobbles[5].logdata.page_data.keys()) == 19


def test_get_author_str_from_row():
    row = [
        1,
        "Test Book",
        "John Smith\nJane Doe",
        0,
        0,
        0,
        300,
        "",
        "",
        "abc123",
        0,
        120,
    ]
    result = get_author_str_from_row(row)
    assert result == "John Smith, Jane Doe"


def test_get_author_str_from_row_strips_middle_initials():
    row = [
        1,
        "Test Book",
        "John A. Smith",
        0,
        0,
        0,
        300,
        "",
        "",
        "abc123",
        0,
        120,
    ]
    result = get_author_str_from_row(row)
    assert result == "John Smith"


@pytest.mark.django_db
def test_lookup_or_create_authors_from_author_str_creates_new_author():
    author = lookup_or_create_authors_from_author_str("New Author")
    assert len(author) == 1
    assert author[0].name == "New Author"


@pytest.mark.django_db
def test_lookup_or_create_authors_from_author_str_finds_existing():
    from books.models import Author

    Author.objects.create(name="Existing Author")
    author = lookup_or_create_authors_from_author_str("Existing Author")
    assert len(author) == 1
    assert author[0].name == "Existing Author"


@pytest.mark.django_db
def test_lookup_or_create_authors_from_author_str_ignores_na():
    author = lookup_or_create_authors_from_author_str("N/A")
    assert len(author) == 0


@pytest.mark.django_db
def test_lookup_or_create_authors_from_author_str_handles_multiple():
    author = lookup_or_create_authors_from_author_str("Author One, Author Two")
    assert len(author) == 2


@pytest.mark.django_db
def test_create_book_from_row():
    row = [
        1,
        "Test Book - Author Name",
        "Test Author",
        0,
        0,
        0,
        300,
        "",
        "",
        "abc123def456",
        3600,
        120,
    ]
    book = create_book_from_row(row)
    assert book.title == "Test Book"
    assert book.pages == 300
    assert "abc123def456" in book.koreader_data_by_hash
    assert book.authors.count() == 1


@pytest.mark.django_db
def test_create_book_from_row_handles_title_with_author_suffix():
    row = [
        1,
        "Test Book - Author_Name",
        "N/A",
        0,
        0,
        0,
        300,
        "",
        "",
        "abc123def456",
        3600,
        120,
    ]
    book = create_book_from_row(row)
    assert book.title == "Test Book"
    assert book.authors.count() == 1
    assert book.authors.first().name == "Author"


@pytest.mark.django_db
def test_create_book_from_row_handles_null_bytes():
    row = [
        1,
        "Test\x00Book",
        "Test\x00Author",
        0,
        0,
        0,
        300,
        "",
        "",
        "abc123def456",
        3600,
        120,
    ]
    book = create_book_from_row(row)
    assert "TestBook" in book.title
    assert "TestAuthor" in book.authors.first().name


@pytest.mark.django_db
def test_build_book_map_ignores_garbage_titles(koreader_rows):
    garbage_row = [
        999,
        "KOReader Quickstart Guide",
        "N/A",
        0,
        0,
        0,
        0,
        "",
        "",
        "garbage123",
        0,
        0,
    ]
    book_map = build_book_map(koreader_rows.BOOK_ROWS + [garbage_row])
    assert len(book_map) == 1
    assert 999 not in book_map
