# Copyright (C) 2015-2026 Benjamin Mako Hill, Nathan TeBlunthuis, Will
# Beason, Sohyeon Hwang, Kaylea Champion, and other contributors
#
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by the
# Free Software Foundation, either version 3 of the License, or (at your
# option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program. If not, see <https://www.gnu.org/licenses/>.

"""
Resume functionality for wikiq JSONL output.

The resume point is derived from the last complete line of the existing
output file, so no separate checkpoint files are needed.
"""

import json
import os
import sys
from collections import deque


def get_jsonl_resume_point(output_file, input_file=None):
    """Get resume point from last complete line of JSONL file.

    For .jsonl.d directories, derives the file path from input_file using get_output_filename.
    """
    # Handle .jsonl.d directory output
    if output_file.endswith('.jsonl.d'):
        if input_file is None:
            return None
        if os.path.isdir(output_file):
            # Import here to avoid circular import
            from wikiq import get_output_filename
            jsonl_filename = os.path.basename(get_output_filename(input_file, 'jsonl'))
            output_file = os.path.join(output_file, jsonl_filename)
            print(f"Looking for resume point in: {output_file}", file=sys.stderr)
        else:
            return None

    if not os.path.exists(output_file):
        return None

    try:
        # Track positions of last two valid lines for potential truncation
        valid_lines = deque(maxlen=2)  # (end_position, record)
        with open(output_file, 'rb') as f:
            while True:
                line = f.readline()
                if not line:
                    break
                try:
                    record = json.loads(line.decode('utf-8'))
                    valid_lines.append((f.tell(), record))
                except (json.JSONDecodeError, KeyError, UnicodeDecodeError):
                    pass

        if not valid_lines:
            return None

        last_valid_pos, last_valid_record = valid_lines[-1]

        # Truncate if file extends past last valid line (corrupted trailing data)
        file_size = os.path.getsize(output_file)
        if last_valid_pos < file_size:
            print(f"Truncating corrupted data from {output_file} ({file_size - last_valid_pos} bytes)", file=sys.stderr)
            with open(output_file, 'r+b') as f:
                f.truncate(last_valid_pos)

        return (last_valid_record['articleid'], last_valid_record['revid'])
    except IOError as e:
        print(f"Warning: Could not read {output_file}: {e}", file=sys.stderr)
        return None


def get_resume_point(output_file, input_file=None):
    """
    Find the resume point from existing JSONL output.

    Args:
        output_file: Path to the output file.
        input_file: Path to input file (needed for .jsonl.d directory output).

    Returns:
        A (pageid, revid) tuple, or None.
    """
    result = get_jsonl_resume_point(output_file, input_file)
    if result:
        print(f"Resume point found from JSONL: pageid={result[0]}, revid={result[1]}", file=sys.stderr)
    return result
