"""Lyrics copyright infringement detection through minhash"""

import logging
import os
import time
import pickle
import re
import string
import json

import modal
from tqdm import tqdm
from suno_utils.utils.s3 import _download_s3_file
from suno_utils.worker.loader import S3Loader
from suno_utils.worker.modal_base import MODAL_MOUNTS, get_modal_base_image
from suno_utils.worker.settings import s3_client
from suno_utils.worker.utils import retry_decorator
from suno_utils.gpt import chirp_v2
from suno_utils.gpt import chirp_v2
from suno_utils.harvest.youtube.constants.base import NON_WHITESPACE_LANGS
from suno_utils.utils.text import normalize_whitespace
from suno_utils.harvest.youtube.language_classify import get_text_lang_p
from suno_utils.worker.schema import QueueItem
from collections import Counter, defaultdict

logger = logging.getLogger(__name__)
logging.basicConfig()
logger.setLevel(logging.INFO)

############## CHANGE THESE ##############
DEPLOYMENT_TYPE = "dev"

##########################################
DEPLOYMENT_TYPES = {"dev", "prod"}
assert DEPLOYMENT_TYPE in DEPLOYMENT_TYPES
APP_NAME = f"detect-lyrics-copyright-infringement-{DEPLOYMENT_TYPE}"
assert APP_NAME.endswith(DEPLOYMENT_TYPE)
MOUNT_PATH = "/suno/models"
UPLOADS_S3_BUCKET = "suno-data-uploads"
MINHASH_NAME = "noempty_minhashes"
LSH_NAME = "noempty_lsh"
INVERTED_INDEX_NAME = "inverted_index_v1"
MINHASH_PATH = os.path.join(MOUNT_PATH, f"{MINHASH_NAME}.pickle")
LSH_PATH = os.path.join(MOUNT_PATH, f"{LSH_NAME}.pickle")
INVERTED_INDEX_PATH = os.path.join(MOUNT_PATH, f"{INVERTED_INDEX_NAME}.pickle")

retry_s3_download = retry_decorator(3, wait_seconds=20)(s3_client.download_fileobj)

N_CPU = 2
aws_secret = modal.Secret.from_name("studio-aws")
DD_SAMPLE_RATE = "1" if DEPLOYMENT_TYPE == "dev" else "0.0001"
SECRETS = [
    aws_secret,
    modal.Secret.from_dict(
        {
            "SUNO_ASSETS_PATH": "/suno/models/assets",
            "XDG_CACHE_HOME": "/suno/models/",
        },
    ),
    modal.Secret.from_name("openai-secret"),
    modal.Secret.from_dict(
        {
            "DD_SITE": "datadoghq.com",
            "DD_ENV": DEPLOYMENT_TYPE,
            "DD_SERVICE": "chatgpt-worker",
            "DD_LOGS_ENABLED": "true",
            "DD_TRACE_ENABLED": "true",
            "DD_TRACE_SAMPLE_RATE": DD_SAMPLE_RATE,
        },
    ),
    modal.Secret.from_name("datadog-metrics"),
    modal.Secret.from_name("api-callback-token"),
]
# for soundclash
WHITELISTED_DOCS = ["doc_1369399", "doc_1892304", "doc_1862978", "doc_8681331"] + [
    "doc_9028903",
    "doc_4340105",
    "doc_5384282",
    "doc_6656167",
    "doc_1797383",
    "doc_7182424",
    "doc_9100110",
    "doc_6217677",
    "doc_6680769",
    "doc_5017032",
    "doc_4173209",
]


class DocumentRetriever:
    def __init__(self, threshold, num_perm=128, seed=42):
        from datasketch import MinHashLSH

        self.threshold = threshold
        self.num_perm = num_perm
        self.seed = seed

        self.text_lang_model = chirp_v2.text_lang_model
        self._minhashes = []
        self._lsh_index = MinHashLSH(threshold=threshold, num_perm=num_perm)

    def load_minhashes_from_file(self, minhash_path: str, lsh_path: str) -> None:
        if not os.path.exists(minhash_path):
            raise FileNotFoundError(f"The file {minhash_path} does not exist.")
        if not os.path.exists(lsh_path):
            raise FileNotFoundError(f"The file {lsh_path} does not exist.")
        print(f"Loading minhashes from {minhash_path} and {lsh_path}")
        start_time = time.time()
        try:
            with open(minhash_path, "rb") as f:
                minhashes = pickle.load(f)
        except pickle.UnpicklingError:
            raise ValueError(f"The file {minhash_path} is not a valid pickle file or is corrupted.")
        except Exception as e:
            raise IOError(f"An error occurred while reading the file: {str(e)}")
        finish_time = time.time()
        print(f"Loaded {len(minhashes)} minhashes in {finish_time - start_time}s")
        start_time = finish_time

        if not isinstance(minhashes, list):
            raise ValueError("The loaded data is not in the expected format (list of MinHash objects).")

        self._minhashes = minhashes

        try:
            with open(lsh_path, "rb") as f:
                lsh_index = pickle.load(f)
        except pickle.UnpicklingError:
            raise ValueError(f"The file {lsh_path} is not a valid pickle file or is corrupted.")
        except Exception as e:
            raise IOError(f"An error occurred while reading the file: {str(e)}")
        finish_time = time.time()
        print(f"Loaded lsh_index in {finish_time - start_time}s")

        self._lsh_index = lsh_index

    def _rebuild_lsh_index(self, minhashes, threshold=0.5):
        from datasketch import MinHashLSH

        start_time = time.time()
        print("Rebuilding LSH index")
        lsh_index = MinHashLSH(threshold=threshold, num_perm=self.num_perm)
        for i, minhash in tqdm(enumerate(minhashes), total=len(minhashes), desc="Building LSH index"):
            lsh_index.insert(f"doc_{i}", minhash)
        end_time = time.time()
        print(f"Rebuilt LSH index in {end_time - start_time}s")
        return lsh_index

    def find_similar_documents(self, doc, threshold=0.5, num_results=20) -> list[tuple[str, float]]:
        similar_doc = []
        minhash = self.compute_minhash(self.clean_text(doc))
        candidates = self._lsh_index.query(minhash)
        for candidate in candidates:
            if candidate in WHITELISTED_DOCS:
                continue
            j = int(candidate.split("_")[1])
            similarity = minhash.jaccard(self._minhashes[j])
            if similarity >= threshold:
                similar_doc.append((candidate, similarity))
        return similar_doc

    def compute_minhash(self, doc):
        """Compute MinHash for a single document."""
        from datasketch import MinHash

        minhash = MinHash(num_perm=self.num_perm, seed=self.seed)
        for shingle in self.shingle_document(doc):
            minhash.update(shingle.encode("utf-8"))
        return minhash

    @staticmethod
    def shingle_document(doc: str, k=3) -> set[str]:
        """Create k-shingles from the document."""
        return set(doc[i : i + k].lower() for i in range(len(doc) - k + 1))

    def clean_text(self, text: str, preserve_whitespace=True) -> str:
        text = text.lower()
        # strip special tags
        text = re.sub(
            r"""
            \(          # Match an opening parenthesis
            \s*         # Match zero or more whitespace characters
            x           # Match the letter 'x'
            \s*         # Match zero or more whitespace characters
            [0-9]+      # Match one or more digits
            \s*         # Match zero or more whitespace characters
            \)          # Match a closing parenthesis
            """,
            " ",  # Replace with a single space
            text,
            flags=re.VERBOSE,
        )
        text = re.sub(
            r"""
            \[          # Match an opening square bracket
            .{1,40}     # Match any character (except newline) 1 to 40 times
            \]          # Match a closing square bracket
            """,
            " ",  # Replace with a single space
            text,
            flags=re.VERBOSE,
        )
        # replace the punctuation with spaces
        trans_table = str.maketrans(string.punctuation, " " * len(string.punctuation))
        text = text.translate(trans_table)
        # do basic clean
        text = normalize_whitespace(text)

        p_lang = get_text_lang_p(self.text_lang_model, text) if self.text_lang_model else {}
        lang_guess, p_lang_guess = self.get_most_likely_language(p_lang)
        # if english do more clean
        if p_lang.get("en", 0) > 0.9:
            text = re.sub(r"[^a-z]", " ", text)
        # if non-whitespace language then artificially make words
        text = normalize_whitespace(text)
        if len(text) == 0:
            return ""
        if (p_lang_guess >= 0.6 and lang_guess in NON_WHITESPACE_LANGS) or (
            text.count(" ") / len(text) == 0.05 and len(text) > 100
        ):
            text = " ".join(list(text))
        # finalize
        text = normalize_whitespace(text)
        if not preserve_whitespace:
            text = re.sub(r"\s", "", text)
        return text

    def get_most_likely_language(self, p_lang: dict[str, float]) -> tuple[str, float]:
        if p_lang:
            # Sort the language-probability pairs by probability
            sorted_langs = sorted(p_lang.items(), key=lambda x: x[1], reverse=True)
            return sorted_langs[0]
        else:
            return ("", 0)


class InvertedIndexForLyrics:
    def __init__(self, k=15, num_buckets=2**18):
        self.lyrics = []
        self.inverted_index = defaultdict(list)
        self.k = k
        self.num_buckets = num_buckets
        self.text_lang_model = chirp_v2.text_lang_model

    def preprocess(self, text):
        return self.clean_text(text)

    def create_shingles(self, text):
        if len(text) < self.k:
            return [text]
        return [text[i : i + self.k] for i in range(len(text) - self.k + 1)]

    def hash_shingle(self, shingle):
        import mmh3

        return mmh3.hash(shingle) % self.num_buckets

    def add_lyrics(self, lyric):
        doc_id = len(self.lyrics)
        processed_text = self.preprocess(lyric)
        shingles = self.create_shingles(processed_text)
        hashed_shingles = [self.hash_shingle(shingle) for shingle in shingles]
        self.lyrics.append(lyric)

        for position, hashed_shingle in enumerate(hashed_shingles):
            self.inverted_index[hashed_shingle].append((doc_id, position))

    def simple_query(self, query):
        processed_query = self.preprocess(query)
        shingles = self.create_shingles(processed_query)
        query_hashed_shingles = [self.hash_shingle(shingle) for shingle in shingles]
        query_tf = Counter(query_hashed_shingles)
        candidate = Counter()

        for hashed_term in query_tf:
            # print(hashed_term, self.inverted_index.get(hashed_term, []))
            candidate_pair = self.inverted_index.get(hashed_term, [])
            candidate.update([c[0] for c in candidate_pair])
        candidate = {k: v / len(query_hashed_shingles) for k, v in candidate.items()}

        candidate = {
            f"pdoc_{k}": v for k, v in candidate.items() if k not in WHITELISTED_DOCS and v > 0.9
        }

        return sorted(candidate.items(), key=lambda x: x[1], reverse=True)[:20]

    def dump(self, filename):
        """
        Dump the inverted index and lyrics to a file using JSON.

        :param filename: The name of the file to save the data to.
        """
        data = {
            "lyrics": self.lyrics,
            "inverted_index": {str(k): v for k, v in self.inverted_index.items()},
            "k": self.k,
            "num_buckets": self.num_buckets,
        }
        with open(filename, "w", encoding="utf-8") as f:
            json.dump(data, f, ensure_ascii=False, indent=2)

    def dump_to_pickle(self, filename):
        data = {
            "lyrics": self.lyrics,
            "inverted_index": {str(k): v for k, v in self.inverted_index.items()},
            "k": self.k,
            "num_buckets": self.num_buckets,
        }
        with open(filename, "wb") as f:
            pickle.dump(data, f)

    @classmethod
    def load(cls, filename):
        """
        Load the inverted index and lyrics from a file using JSON.

        :param filename: The name of the file to load the data from.
        :return: An instance of InvertedIndexForLyrics with the loaded data.
        """
        with open(filename, "r", encoding="utf-8") as f:
            data = json.load(f)

        instance = cls(k=data["k"], num_buckets=data["num_buckets"])
        instance.lyrics = data["lyrics"]
        instance.inverted_index = defaultdict(
            list, {int(k): v for k, v in data["inverted_index"].items()}
        )

        return instance

    @classmethod
    def load_from_pickle(cls, filename):
        with open(filename, "rb") as f:
            data = pickle.load(f)

        print(f"Loading from Pickle:Inverted index size: {len(data['inverted_index'])}")

        instance = cls(k=data["k"], num_buckets=data["num_buckets"])
        instance.lyrics = data["lyrics"]
        instance.inverted_index = defaultdict(
            list, {int(k): v for k, v in data["inverted_index"].items()}
        )
        return instance

    def clean_text(self, text: str, preserve_whitespace=True) -> str:
        text = text.lower()
        # strip special tags
        text = re.sub(
            r"""
            \(          # Match an opening parenthesis
            \s*         # Match zero or more whitespace characters
            x           # Match the letter 'x'
            \s*         # Match zero or more whitespace characters
            [0-9]+      # Match one or more digits
            \s*         # Match zero or more whitespace characters
            \)          # Match a closing parenthesis
            """,
            " ",  # Replace with a single space
            text,
            flags=re.VERBOSE,
        )
        text = re.sub(
            r"""
            \[          # Match an opening square bracket
            .{1,40}     # Match any character (except newline) 1 to 40 times
            \]          # Match a closing square bracket
            """,
            " ",  # Replace with a single space
            text,
            flags=re.VERBOSE,
        )
        # replace the punctuation with spaces
        trans_table = str.maketrans(string.punctuation, " " * len(string.punctuation))
        text = text.translate(trans_table)
        # do basic clean
        text = normalize_whitespace(text)

        p_lang = get_text_lang_p(self.text_lang_model, text) if self.text_lang_model else {}
        lang_guess, p_lang_guess = self.get_most_likely_language(p_lang)
        # if english do more clean
        if p_lang.get("en", 0) > 0.9:
            text = re.sub(r"[^a-z]", " ", text)
        # if non-whitespace language then artificially make words
        text = normalize_whitespace(text)
        if len(text) == 0:
            return ""
        if (p_lang_guess >= 0.6 and lang_guess in NON_WHITESPACE_LANGS) or (
            text.count(" ") / len(text) == 0.05 and len(text) > 100
        ):
            text = " ".join(list(text))
        # finalize
        text = normalize_whitespace(text)
        if not preserve_whitespace:
            text = re.sub(r"\s", "", text)
        return text

    def get_most_likely_language(self, p_lang: dict[str, float]) -> tuple[str, float]:
        if p_lang:
            # Sort the language-probability pairs by probability
            sorted_langs = sorted(p_lang.items(), key=lambda x: x[1], reverse=True)
            return sorted_langs[0]
        else:
            return ("", 0)


class LyricsCopyrightDetectorWorker(S3Loader):
    """Detect lyrics copyright infringement through minhash"""

    def __init__(
        self,
        lsh_threshold: float = 0.5,
        similarity_threshold: float = 0.9,
        n_matches: int = 1,
        version_name: str = "v2-large",
    ):
        self.lsh_threshold = lsh_threshold
        self.similarity_threshold = similarity_threshold
        self.n_matches = n_matches
        self.existing_lyrics_index = DocumentRetriever(threshold=self.lsh_threshold)
        self.popular_inverted_index = None
        self.version_name = version_name

    def preload(self):
        start_time = time.time()
        ckpt_path = chirp_v2._get_model_if_needed(chirp_v2.FASTTEXT_CKPT_PATH, cache_dir=MOUNT_PATH)
        chirp_v2.load_fasttext_model(ckpt_path)
        self.existing_lyrics_index.load_minhashes_from_file(MINHASH_PATH, LSH_PATH)

        finish_time = time.time()
        print(f"Preloading minhash and lsh took {finish_time - start_time}s")
        start_time = time.time()
        self.popular_inverted_index = InvertedIndexForLyrics.load_from_pickle(INVERTED_INDEX_PATH)
        finish_time = time.time()
        print(f"Preloading inverted index took {finish_time - start_time}s")
        print(f"Inverted index size: {len(self.popular_inverted_index.inverted_index)}")

    def is_lyrics_copyright_infringement(self, lyrics: str) -> tuple[bool, str, str]:
        if self.existing_lyrics_index is None:
            print("Lyrics index not initialized")
            return False, str({}), self.version_name
        result = self.existing_lyrics_index.find_similar_documents(
            lyrics, threshold=self.similarity_threshold
        )
        if len(result) >= self.n_matches:
            return True, str(result), self.version_name
        return False, str(result), self.version_name

    def is_lyrics_copyright_infringement_v2(self, lyrics: str) -> tuple[bool, str, str]:
        if self.existing_lyrics_index is None:
            print("Lyrics index not initialized")
            return False, str({}), self.version_name
        result = self.existing_lyrics_index.find_similar_documents(
            lyrics, threshold=self.similarity_threshold
        )
        if self.popular_inverted_index is not None:
            partial_match_result = self.popular_inverted_index.simple_query(lyrics)
            result = result + partial_match_result
        if len(result) >= self.n_matches:
            return True, str(result), self.version_name
        return False, str(result), self.version_name

    def is_lyrics_copyright_infringement_with_details(self, lyrics: str, threshold: float = 0.9) -> dict:
        if self.existing_lyrics_index is None:
            print("Lyrics index not initialized")
            return {"result": [], "version": self.version_name, "error": "Lyrics index not initialized"}
        result = self.existing_lyrics_index.find_similar_documents(lyrics, threshold=threshold)
        return {"result": result, "version": self.version_name, "model": "minhash"}

    def is_lyrics_copyright_infringement_with_details_v2(
        self, lyrics: str, threshold: float = 0.9
    ) -> dict:
        if self.existing_lyrics_index is None:
            print("Lyrics index not initialized")
            return {"result": [], "version": self.version_name, "error": "Lyrics index not initialized"}
        result = self.existing_lyrics_index.find_similar_documents(lyrics, threshold=threshold)
        if self.popular_inverted_index is not None:
            partial_match_result = self.popular_inverted_index.simple_query(lyrics)
            result = result + partial_match_result
        return {"result": result, "version": self.version_name, "model": "minhash"}

    def is_lyrics_copyright_infringement_with_popular_index(self, lyrics: str) -> dict:
        if self.popular_inverted_index is None:
            print("Popular inverted index not initialized")
            return {
                "result": [],
                "version": self.version_name,
                "error": "Popular inverted index not initialized",
            }
        result = self.popular_inverted_index.simple_query(lyrics)
        return {"result": result, "version": self.version_name, "model": "inverted_index"}


def download_minhash_wrapper() -> None:
    start_time = time.time()
    target_minhash_path = os.path.join(MOUNT_PATH, f"{MINHASH_NAME}.pickle")
    print("Downloading minhash files")
    _download_s3_file(
        f"s3://suno-data/ashe/trained_models/{MINHASH_NAME}.pickle",
        target_minhash_path,
    )
    finish_time = time.time()
    print(f"Downloaded minhash files successfully in {finish_time - start_time}s")
    start_time = finish_time
    target_lsh_path = os.path.join(MOUNT_PATH, f"{LSH_NAME}.pickle")
    print("Downloading lsh index files")
    _download_s3_file(
        f"s3://suno-data/ashe/trained_models/{LSH_NAME}.pickle",
        target_lsh_path,
    )
    finish_time = time.time()
    print(f"Downloaded lsh index files successfully in {finish_time - start_time}s")
    start_time = finish_time
    target_inverted_index_path = os.path.join(MOUNT_PATH, f"{INVERTED_INDEX_NAME}.pickle")
    print("Downloading inverted index files")
    _download_s3_file(
        f"s3://suno-data/ashe/trained_models/{INVERTED_INDEX_NAME}.pickle",
        target_inverted_index_path,
    )
    finish_time = time.time()
    print(f"Downloaded inverted index files successfully in {finish_time - start_time}s")


base_image = get_modal_base_image().pip_install("datasketch").pip_install("mmh3")
image = base_image.run_function(download_minhash_wrapper, secrets=SECRETS)
app = modal.App(APP_NAME, image=image)


@app.cls(
    cpu=N_CPU,
    secrets=SECRETS,
    timeout=4000,
    scaledown_window=1200,
    mounts=MODAL_MOUNTS,
    retries=modal.Retries(
        max_retries=2,
        backoff_coefficient=2.0,
        initial_delay=5.0,
    ),
    memory=250000,
    min_containers=25,
    allow_concurrent_inputs=60,
)
class LyricsCopyrightDetectStub:
    """App for detecting lyrics copyright infringement"""

    def __init__(self):
        """Set up LyricsCopyrightDetectApp."""
        self.worker = LyricsCopyrightDetectorWorker()
        self.worker.preload()

    @modal.method()
    def copyright_detection(self, queue_item: str):
        item = QueueItem(**json.loads(queue_item))
        lyrics = item.prompt_text or ""
        if len(lyrics) == 0:
            return False
        result = self.worker.is_lyrics_copyright_infringement(lyrics)
        if item.callback_url:
            self.worker.notify_finish(
                QueueItem(id=item.id, metadata={}, callback_url=item.callback_url),
                {
                    "id": item.id,
                    "clip_ids": item.ids,
                    "copyright_infringement": result[0],
                    "copyright_infringement_details": result[1],
                    "model_name": result[2],
                },
            )
        return result[0]

    @modal.method()
    def copyright_detection_v2(self, queue_item: str):
        item = QueueItem(**json.loads(queue_item))
        lyrics = item.prompt_text or ""
        if len(lyrics) == 0:
            return False
        result = self.worker.is_lyrics_copyright_infringement_v2(lyrics)
        if item.callback_url:
            self.worker.notify_finish(
                QueueItem(id=item.id, metadata={}, callback_url=item.callback_url),
                {
                    "id": item.id,
                    "clip_ids": item.ids,
                    "copyright_infringement": result[0],
                    "copyright_infringement_details": result[1],
                    "model_name": result[2],
                },
            )
        return result[0]

    @modal.method()
    def copyright_detection_with_details(self, lyrics: str, threshold: float = 0.9):
        if len(lyrics) == 0:
            return None
        result = self.worker.is_lyrics_copyright_infringement_with_details(lyrics, threshold)
        return result

    @modal.method()
    def copyright_detection_with_details_v2(self, lyrics: str, threshold: float = 0.9):
        if len(lyrics) == 0:
            return None
        result = self.worker.is_lyrics_copyright_infringement_with_details_v2(lyrics, threshold)
        return result

    @modal.method()
    def copyright_detection_with_popular_index(self, lyrics: str):
        if len(lyrics) <= 100:
            return None
        result = self.worker.is_lyrics_copyright_infringement_with_popular_index(lyrics)
        return result


@app.local_entrypoint()
def main():
    testApp = LyricsCopyrightDetectStub()
    test_data = {
        "id": "123",
        "prompt_text": "my lyrics",
        "metadata": {"tags": "r&b"},
    }
    result = testApp.copyright_detection.remote(json.dumps(test_data))
    print(f"Test1: lyrics copyright infringement result: {result}")

    existing_lyrics = "it might seem crazy what i am about to say sunshine she s here you can take a break i m a hot air balloon that could go to space with the air like i don t care baby by the way because i m happy clap along if you feel like a room without a roof because i m happy clap along if you feel like happiness is the truth because i m happy clap along if you know what happiness is to you because i m happy clap along if you feel like that s what you wanna do here come bad news talking this and that yeah give me all you got don t hold back yeah well i should probably warn you i ll be just fine yeah no offense to you don t waste your time here s why because i m happy clap along if you feel like a room without a roof because i m happy clap along if you feel like happiness is the truth because i m happy clap along if you know what happiness is to you because i m happy clap along if you feel like that s what you wanna do happy bring me down can t nothin happy bring me down my love is to high happy bring me down can t nothin happy bring me down let me tell you now happy happy happy happy bring me down can t nothin happy happy happy happy bring me down my love is too high happy happy happy happy bring me down can t nothin happy happy happy happy bring me down i said because i m happy clap along if you feel like a room without a roof because i m happy clap along if you feel like happiness is the truth because i m happy clap along if you know what happiness is to you because i m happy clap along if you feel like that s what you wanna do because i m happy clap along if you feel like a room without a roof because i m happy clap along if you feel like happiness is the truth because i m happy clap along if you know what happiness is to you because i m happy clap along if you feel like that s what you wanna do happy happy happy happy bring me down can t nothin happy happy happy happy bring me down my love is too high happy happy happy happy bring me down can t nothin happy happy happy happy bring me down i said because i m happy clap along if you feel like a room without a roof because i m happy clap along if you feel like happiness is the truth because i m happy clap along if you know what happiness is to you because i m happy clap along if you feel like that s what you wanna do because i m happy clap along if you feel like a room without a roof because i m happy clap along if you feel like happiness is the truth because i m happy clap along if you know what happiness is to you because i m happy clap along if you feel like that s what you wanna do"
    test_existing_data = {
        "id": "456",
        "prompt_text": existing_lyrics,
        "metadata": {"tags": "jazz"},
    }
    result = testApp.copyright_detection.remote(json.dumps(test_existing_data))
    print(f"Test2: positive lyrics copyright infringement result: {result}")

    result = testApp.copyright_detection_with_details.remote(json.dumps(test_existing_data))
    print(f"Test2: positive lyrics copyright infringement detailed result: {result}")

    existing_lyrics_short = """
        [Verse]
        I've created a monster
        'Cause nobody wants to see Marshall no more, they want Shady, I'm chopped liver
        Well, if you want Shady, this is what I'll give ya
        A little bit of weed mixed with some hard liquor
        Some vodka that'll jump-start my heart quicker
        Than a shock when I get shocked at the hospital
        By the doctor when I'm not cooperating
        When I'm rockin' the table while he's operating (Hey!)
        You waited this long, now stop debating
        'Cause I'm back, I'm on the rag and ovulating
        I know that you got a job, Ms. Cheney
        But your husband's heart problem's complicating
        So the FCC won't let me be
        Or let me be me, so let me see
        They tried to shut me down on MTV
        But it feels so empty without me
        So come on and dip, bum on your lips
        Fuck that, cum on your lips and some on your tits
        And get ready, 'cause this shit's about to get heavy
        I just settled all my lawsuits (Fuck you, Debbie!)
    """

    result = testApp.copyright_detection_with_details.remote(existing_lyrics_short)
    print(f"Test3: positive lyrics copyright infringement detailed result: {result}")

    result = testApp.copyright_detection_with_details_v2.remote(existing_lyrics_short)
    print(f"Test3: positive lyrics copyright infringement detailed v2 result: {result}")

    result = testApp.copyright_detection_v2.remote(json.dumps(test_existing_data))
    print(f"Test3: positive lyrics copyright infringement v2 result: {result}")

    result = testApp.copyright_detection_with_popular_index.remote(existing_lyrics_short)
    print(f"Test3: positive lyrics copyright infringement popular index result: {result}")

    test_lyrics = """[Verse]
    一盏离愁孤灯伫立在窗口
    我在门后假装你人还没走
    旧地如重游月圆更寂寞
    夜半清醒的烛火不忍苛责我

    [Verse]
    一壶漂泊浪迹天涯难入喉
    你走之后酒暖回忆思念瘦
    水向东流时间怎么偷
    花开就一次成熟我却错过

    [Chorus]
    谁在用琵琶弹奏一曲东风破
    岁月在墙上剥落看见小时候
    犹记得那年我们都还很年幼
    而如今琴声幽幽我的等候你没听过"""
    result = testApp.copyright_detection_with_popular_index.remote(test_lyrics)
    print(f"Test4: positive lyrics copyright infringement popular index result: {result}")

    result = testApp.copyright_detection_v2.remote(json.dumps(test_data))
    print(f"Test4: lyrics copyright infringement v2 result: {result}")
