# -*- coding: UTF-8 -*-

__revision__ = '$Id: PluginMovieIMDB.py 1660 2014-03-13 20:48:05Z mikej06 $'

# Copyright (©) 2005-2025 V.Nunes, P. Ożarowski, A. Flöter, W. Friebel
#
# 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 2 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 Library General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA

# You may use and distribute this software under the terms of the
# GNU General Public License, version 2 or later

import gutils
import movie
import re
from gettext import gettext as _
from bs4 import BeautifulSoup
from dataclasses import dataclass
import logging
import requests

log = logging.getLogger('Griffith')

plugin_name = 'IMDb'
plugin_description = 'Internet Movie Database'
plugin_url = 'www.imdb.com'
plugin_language = _('English')
plugin_author = 'Vasco Nunes, Piotr Ożarowski, Andreas Flöter, W. Friebel'
plugin_author_email = 'wp.friebel@gmail.com'
plugin_version = '1.22'


def before_more(data):
    for element in ['>See more<', '>more<', '>Full summary<', '>Full synopsis<']:
        tmp = data.find(element)
        if tmp > 0:
            data = data[:tmp] + '>'
    return data


def read_page(filename):
    with open(filename, 'r') as fp:
        return fp.read()


def is_genres_span(tag):
    return tag.name == 'span' and tag.text.strip() == 'Genres'


def is_director_span(tag):
    return tag.name == 'span' and tag.text.strip() == 'Director'


def is_language_tag(tag):
    # if tag.name == 'a' and '/search/title?title_type=feature&primary_language' in tag['href']:
    #     print(f"tag.name={tag.name}, tag['href']={tag['href']}")
    return tag.name == 'a' and '/search/title?title_type=feature&primary_language' in tag['href']


def is_mediaviewer_tag(tag):
    # if tag.name == 'a' and 'mediaviewer' in tag['href']:
    #     print(f"tag.name={tag.name}, tag['href']={tag['href']}")
    return tag.name == 'a' and 'mediaviewer' in tag['href']


def is_storyline_tag(tag):
    # <li class="ipc-metadata-list__item ipc-metadata-list-item--link" data-testid="storyline-certificate" role="presentation">
    # if (tag.name == 'li' and 'data-testid' in tag.attrs.keys()
    #         and 'storyline-certificate' in tag.attrs['data-testid']):
    #     print(f"tag.name={tag.name} {tag.attrs}")
    return (tag.name == 'li' and 'data-testid' in tag.attrs.keys()
            and 'storyline-certificate' in tag.attrs['data-testid'])


def is_studio_tag(tag):
    # <li role="presentation" class="ipc-metadata-list__item ipc-metadata-list-item--link" data-testid="title-details-companies">
    # if tag.name == 'li' and 'data-testid' in tag.attrs.keys()
    #     and 'title-details-companies' in tag.attrs['data-testid']:
    #     print(f"tag.name={tag.name} {tag.attrs}")
    return (tag.name == 'li' and 'data-testid' in tag.attrs.keys()
            and 'title-details-companies' in tag.attrs['data-testid'])


def is_plot_tag(tag):
    return (tag.name == 'div' and 'data-testid' in tag.attrs.keys()
            and 'sub-section-summaries' in tag.attrs['data-testid'])


@dataclass
class MovieInfo:
    title: str
    movie_id: str
    extra_info: str


class Plugin(movie.Movie):
    def __init__(self, movie_id):
        self.page_main = None
        self.page_cast = None
        self.page_plot = None
        self.encode = 'utf-8'
        self.movie_id = movie_id
        self.url = f"https://www.imdb.com/title/tt{self.movie_id}/"
        self.soup = None
        self.soup_cast = None

    def initialize(self):
        self.page_cast = self.open_page(url=self.url + 'fullcredits')
        self.page_main = self.open_page(url=self.url)
        self.page_plot = self.open_page(url=self.url + 'plotsummary')
        # self.comp_page = self.open_page(url=self.url + 'companycredits')
        # self.tagl_page = self.open_page(url=self.url + 'taglines')
        # self.cert_page = self.open_page(url=self.url + 'parentalguide')
        # self.release_page = self.open_page(url=self.url + 'releaseinfo')
        self.soup = BeautifulSoup(self.page_main, 'html.parser')
        self.soup_cast = BeautifulSoup(self.page_cast, 'html.parser')
        self.soup_plot = BeautifulSoup(self.page_plot, 'html.parser')

    def get_image(self):
        url_pattern = re.compile(r'^((https://www.imdb.com)?/title/tt\d+/mediaviewer/rm\d+/)(\?ref_=tt_ov_i)?$')
        self.image_url = ''
        image_page_url = ''

        for tag in self.soup.find_all(is_mediaviewer_tag):
            match = url_pattern.match(tag['href'])
            if match:
                image_page_url = match.groups()[0]
                log.debug(f"URL candidate: {match.groups()[0]}")
                if match.groups()[2]:
                    if not match.groups()[1]:
                        image_page_url = f"https://www.imdb.com{image_page_url}"
                    # self.image_url = image_page_url
                    log.debug(f"PluginMovieIMDB: Image Page URL: {image_page_url}")
                    break
                else:
                    log.debug(f"Not correct URL found: {match.groups()[0]}")
        image_page = requests.get(image_page_url,
                                  headers=self.config.http_header)
        if image_page.status_code != 200:
            log.debug(f"Unable to retrieve image page: {image_page_url}, "
                      f"code:{image_page.status_code}")
            return
        soup_image_page = BeautifulSoup(image_page.content, 'html.parser')
        log.debug(f"soup_image_page: {len(soup_image_page.body)}")
        # <img alt="Gwyneth Paltrow and Joseph Fiennes in Shakespeare in Love (1998)"
        # class="sc-7c0a9e7c-0 eWmrns" data-image-id="rm381097216-curr"
        # sizes="100vw" src="https://m.media-amazon.com/images/M/MV5BM2ZkNjM5M
        # jEtNTBlMC00OTI5LTgyYmEtZDljMzNmNzhiNzY0XkEyXkFqcGdeQXVyNDYyMDk5MTU@.
        # _V1_.jpg"
        for img_tag in soup_image_page.find_all('img', {'class': 'sc-7c0a9e7c-0'}):
            image_url = img_tag['src']
            log.debug(f"image_url: {image_url}")
            if image_url.endswith('.jpg'):
                self.image_url = image_url
                log.debug(f"Found image_url: {image_url}")
            break

    def get_o_title(self):
        self.o_title = gutils.trim(self.page_main, '<div class="originalTitle">', '<span')
        if not self.o_title:
            self.o_title = gutils.regextrim(self.page_main, '<h1 itemprop="name"[^>]*>', '&nbsp;')
        if not self.o_title:
            self.o_title = gutils.trim(self.page_main, 'og:title\' content="', '"')
        if not self.o_title:
            self.o_title = re.sub(' [(].*', '', gutils.trim(self.page_main, '<title>', '</title>'))
        self.o_title = gutils.clean(re.sub('"', '', self.o_title))

    def get_title(self):
        title_section = self.soup.find('h1', {'data-testid': 'hero__pageTitle'})
        if title_section:
            title = title_section.find('span', {'class': 'hero__primary-text'})
            if title:
                self.title = title.text.strip()
            else:
                self.title = ''

    def get_director(self):
        self.director = ''
        directors = []
        div_tag = None
        for tag in self.soup.find_all(is_director_span):
            div_tag = tag.next_sibling
            log.debug(f"div_tag: {div_tag}")
            break
        if div_tag:
            for a_tag in div_tag.find_all(
                    'a',
                    {'class': 'ipc-metadata-list-item__list-content-item '
                              'ipc-metadata-list-item__list-content-item--link'}):
                directors.append(a_tag.text.strip())
            if directors:
                self.director = ', '.join(directors)

    def get_plot(self):
        self.plot = ''
        plot_summaries = []
        div_summaries_tag = self.soup_plot.find(is_plot_tag)
        if div_summaries_tag:
            for div_tag in div_summaries_tag.find_all(
                    'div',
                    {'class': 'ipc-html-content-inner-div'}):
                plot_summary = div_tag.find('div', {'role': 'presentation'})
                if (plot_summary):
                    plot_summary = plot_summary.text.strip()
                    plot_summary = plot_summary.replace(r"\n", ' ')
                    pos_author = plot_summary.find('—')
                    plot_summary = plot_summary[:pos_author]
                    a_author_tag = div_tag.find('a', {'class': 'ipc-link ipc-link--base'})
                    if a_author_tag:
                        author = a_author_tag.text.strip()
                        plot_summaries.append(f"{plot_summary}\nAuthor: {author}")
                        log.debug(f"{plot_summary}\nAuthor: {author}")
                    else:
                        log.debug("No author 'a' tag found")
                        plot_summaries.append(f"{plot_summary}")
                        log.debug(f"{plot_summary}")
            self.plot = '\n\n'.join(plot_summaries)

    def get_year(self):
        self.year = gutils.trim(self.page_main, '<a href="/year/', '</a>')
        self.year = gutils.after(self.year, '>')
        if not self.year:
            tmp = gutils.trim(self.page_main, '<title>', '</title>')
            tmp = re.search('([0-9]{4})', tmp)
            if tmp:
                self.year = tmp.group(0)

    def get_runtime(self):
        runtime_hm_pattern = re.compile(r'(\d+).*hour\D+(\d+)\D+minutes')
        runtime_h_pattern = re.compile(r'(\d+).*hour\D*')
        hours, minutes = 0, 0

        # <div data-testid="title-techspecs-section" class="sc-f65f65be-0 bBlII">
        tec_section = self.soup.find('div', {'data-testid': 'title-techspecs-section'})
        # print(f"\ntec_section:\n{tec_section}")
        if tec_section:
            # <div class="ipc-metadata-list-item__content-container">1<!-- --> <!-- -->hour<!-- --> <!-- -->55<!-- --> <!-- -->minutes</div>
            tec_topics = tec_section.find_all(
                'div', {'class': 'ipc-metadata-list-item__content-container'})
            # print(f"\ntec_topics:\n{tec_topics}")
            for tec_topic in tec_topics:
                match = runtime_hm_pattern.match(tec_topic.text)
                # print(f"_____ match:\n{match}")
                if match:
                    hours = int(match.groups()[0])
                    minutes = int(match.groups()[1])
                    break
                else:
                    # print(f"tech_topic={tec_topic.text}")
                    match = runtime_h_pattern.match(tec_topic.text)
                    if match:
                        # print(f"________ match={match}, {match.groups()[0]}")
                        hours = int(match.groups()[0])
                        break
        if hours > 0 or (hours == 0 and minutes > 0):
            self.runtime = hours * 60 + minutes

    def get_genre(self):
        self.genre = ''
        genres = ''
        pattern = re.compile('^.+"genre":\[([^]]+)].+$', re.DOTALL)
        match = pattern.match(self.page)
        if match:
            genres = match.groups()[0]
            genres = genres.replace('"', '')
            genres = ', '.join(genres.split(','))
            self.genre = genres

    def get_cast(self):
        self.cast = ''
        self.cast = gutils.trim(self.page_cast, '<table class="cast_list">', '</table>')
        if self.cast == '':
            self.cast = gutils.trim(self.page_main, '<table class="cast">', '</table>')
        self.cast = self.cast.replace(' ... ', _(' as '))
        self.cast = self.cast.replace('...', _(' as '))
        self.cast = self.cast.replace("\n", '')
        self.cast = self.cast.replace('</tr>', "\n")
        self.cast = before_more(self.cast)
        self.cast = gutils.clean(self.cast)
        self.cast = re.sub('[ \t]+', ' ', self.cast)
        self.cast = re.sub('( *\n *)+', '\n', self.cast)
        # self.cast = re.sub(' *\n\n *', '\n', self.cast)
        self.cast = self.cast.strip()

    def get_classification(self):
        """
        The url provides the classification in one or more countries. It would
        be a lot more effort to parse this page since the country to be used
        must be defined and a fallback if no country is defined or the country
        which is defined is not in the list of classifications.
        """
        self.classification = ''

    def get_studio(self):
        studio_list = []
        self.studio = ''
        studio_tag = self.soup.find(is_studio_tag)
        if studio_tag:
            studios = studio_tag.find_all(
                'a',
                {'class': "ipc-metadata-list-item__list-content-item "
                          "ipc-metadata-list-item__list-content-item--link"})
            for studio in studios:
                # print(f"studio.text={studio.text}")
                if studio.text.strip():
                    studio_list.append(studio.text.strip())
        self.studio = ', '.join(studio_list)

    def get_o_site(self):
        self.o_site = ''

    def get_site(self):
        self.site = f"https://www.imdb.com/title/tt{self.movie_id}/"

    def get_trailer(self):
        """
        Sets the trailer URL but does not check if actually trailers exist.
        """
        self.trailer = f"https://www.imdb.com/title/tt{self.movie_id}/trailers"

    def get_country(self):
        """not used"""
        self.country = gutils.after(gutils.trim(self.page_main, 'country_of_origin', '</a>'), '>')

    def get_rating(self):
        rating = gutils.strip_tags(gutils.trim(self.page_main, 'IMDb RATING', '</span>'))
        if rating:
            try:
                # self.rating = round(float(rating), 0)
                self.rating = float(rating)
            except ValueError as _exc:
                self.rating = 0
        else:
            self.rating = 0

    def get_notes(self):
        self.notes = ''
        color_value = ''
        sound_value = []
        language_value = ''

        a_language_tag = self.soup.find(is_language_tag)
        if a_language_tag:
            language_value = a_language_tag.text
        language = language_value

        # <div data-testid="title-techspecs-section" class="sc-f65f65be-0 bBlII">
        tec_section = self.soup.find('div', {'data-testid': 'title-techspecs-section'})
        # print(f"\ntec_section:\n{tec_section}")
        if tec_section:
            # <a aria-disabled="false" class="ipc-metadata-list-item__list-content-item ipc-metadata-list-item__list-content-item--link" href="/search/title/?colors=color&amp;ref_=tt_spec_att" role="button" tabindex="0">Color</a>
            a_tags = tec_section.find_all(
                'a', {'class': r'ipc-metadata-list-item__list-content-item ipc-metadata-list-item__list-content-item--link'})
            for a_tag in a_tags:
                if 'colors=' in a_tag['href']:
                    # href="/search/title/?colors=color&ref_=tt_spec_att"
                    color_value = a_tag.text.strip()
                if 'sound_mixes=' in a_tag['href']:
                    # href="/search/title/?sound_mixes=stereo&ref_=tt_spec_att"
                    sound_value.append(a_tag.text.strip())
        color = color_value
        sound = ', '.join(sound_value)

        tagline = ''
        note_list = []
        if len(language) > 0:
            note_list.append("%s: %s" % (_('Language'), language))
        if len(sound) > 0:
            note_list.append("%s: %s" % (gutils.strip_tags(_('<b>Audio</b>')), sound))
        if len(color) > 0:
            note_list.append("%s: %s" % (_('Color'), color))
        if len(tagline) > 0:
            note_list.append("%s: %s" % ('Tagline', tagline))
        self.notes = '\n'.join(note_list)

    def get_screenplay(self):
        self.screenplay = ''
        # parts = re.split('<a href=', gutils.trim(self.page_cast, '>Writing Credits', '</table>'))
        # if len(parts) > 1:
        #     for part in parts[1:]:
        #         screenplay = gutils.trim(part, '>', '<')
        #         screenplay = screenplay.replace('\n', '')
        #         screenplay = screenplay.strip()
        #         if screenplay == 'WGA':
        #             continue
        #         screenplay = screenplay.replace(' (written by)', '')
        #         screenplay = screenplay.replace(' and<', '<')
        #         if screenplay not in self.screenplay:
        #             self.screenplay = self.screenplay + screenplay + ', '
        #     if len(self.screenplay) > 2:
        #         self.screenplay = self.screenplay[0:len(self.screenplay) - 2]
        #         self.screenplay = re.sub('[ \t]+', ' ', self.screenplay)

    def get_cameraman(self):
        """not used"""
        self.cameraman = ''
        # tmp = gutils.regextrim(self.page_cast, '>Cinematography by', '</table>')
        # tmp = tmp.split('href="')
        # if len(tmp) > 1:
        #     for entry in tmp[1:]:
        #         entry = gutils.trim(entry, '>', '<')
        #         entry = entry.replace('\n', '')
        #         entry = entry.strip()
        #         if entry:
        #             self.cameraman = self.cameraman + entry + ', '
        #     if self.cameraman:
        #         self.cameraman = self.cameraman[:-2]


class SearchPlugin(movie.SearchMovie):

    def __init__(self):
        """
        https://www.imdb.com/find?s=tt;q=
        Seems to give the best results. 25 results for "Damages", popular titles
        first.
        """
        super().__init__()
        self.original_url_search = 'https://www.imdb.com/find?s=tt&q='
        self.translated_url_search = 'https://www.imdb.com/find?s=tt&q='
        self.encode = 'utf8'
        self.page = None

    def search(self, parent_window):
        if not self.open_search(parent_window):
            return None
        return self.page

    def get_searches(self):
        movie_id_pattern = re.compile(r'/title/tt(\d+).*$')
        soup = BeautifulSoup(self.page, 'html.parser')
        # <div class="ipc-metadata-list-summary-item__tc">
        movie_div_items = soup.find_all(
            'div',
            {'class': 'ipc-metadata-list-summary-item__tc'})
        for movie_item in movie_div_items:
            # <div class="ipc-metadata-list-summary-item__tc">
            movie_entry = movie_item.find(
                'a', {'class': 'ipc-metadata-list-summary-item__t'})
            movie_title = movie_entry.text.strip()
            movie_id = movie_entry['href']
            match = movie_id_pattern.match(movie_id)
            if match:
                movie_id = match.groups()[0]
            else:
                movie_id = ''
            # <li role="presentation" class="ipc-inline-list__item">
            movie_additional_infos = movie_item.find_all(
                'li', {'class': 'ipc-inline-list__item'})
            infos = []
            for additional_info in movie_additional_infos:
                # <span class="ipc-metadata-list-summary-item__li" aria-disabled="false">
                info = additional_info.find(
                    'span', {'class': 'ipc-metadata-list-summary-item__li'})
                if info:
                    infos.append(info.text)
            movie_info = MovieInfo(movie_title, movie_id, '|'.join(infos))
            self.ids.append(movie_info.movie_id)
            self.titles.append(' - '.join((movie_info.title,
                                           movie_info.extra_info)))
            # log.debug(f"movie={movie}\n____")


class SearchPluginTest(SearchPlugin):
    """
    Plugin test for the search plugin
    Configuration for automated tests:
    dict { movie_id -> [ expected result count for original url,
                         expected result count for translated url ] }
    """
    test_configuration = {
        'Damages': [25, 25],
        'Ein glückliches Jahr': [17, 17],
        'Shakespeare in Love': [25, 25],
        }


class PluginTest:
    """
    Plugin test for the extraction of content for a film
    Configuration for automated tests:
    dict { movie_id -> dict { arribute -> value } }

    value: * True/False if attribute only should be tested for any value
           * or the expected value
    """
    test_configuration = {
        '0138097': {
            'title': 'Shakespeare in Love',
            'o_title': 'Shakespeare in Love',
            'director': 'John Madden',
            'plot': True,
            'cast': """Geoffrey Rush as Philip Henslowe
Tom Wilkinson as Hugh Fennyman
Steven O'Donnell as Lambert
Tim McMullan as Frees (as Tim McMullen)
Joseph Fiennes as Will Shakespeare
Steven Beard as Makepeace, the Preacher
Antony Sher as Dr Moth
Patrick Barlow as Will Kempe
Martin Clunes as Richard Burbage
Sandra Reinton as Rosaline
Simon Callow as Tilney, Master of the Revels
Judi Dench as Queen Elizabeth
Bridget McConnell as Lady in Waiting (as Bridget McConnel)
Georgie Glen as Lady in Waiting
Nicholas Boulton as Henry Condell
Gwyneth Paltrow as Viola De Lesseps
Imelda Staunton as Nurse
Colin Firth as Lord Wessex
Desmond McNamara as Crier
Barnaby Kay as Nol
Jim Carter as Ralph Bashford
Paul Bigley as Peter, the Stage Manager
Jason Round as Actor in Tavern
Rupert Farley as Barman
Adam Barker as First Auditionee
Joe Roberts as John Webster
Harry Gostelow as Second Auditionee
Alan Cody as Third Auditionee
Mark Williams as Wabash
David Curtiz as John Hemmings
Gregor Truter as James Hemmings
Simon Day as First Boatman
Jill Baker as Lady De Lesseps
Amber Glossop as Scullery Maid
Robin Davies as Master Plum
Hywel Simons as Servant
Nicholas Le Prevost as Sir Robert De Lesseps
Ben Affleck as Ned Alleyn
Timothy Kightley as Edward Pope
Mark Saban as Augustine Philips
Bob Barrett as George Bryan
Roger Morlidge as James Armitage
Daniel Brocklebank as Sam Gosse
Roger Frost as Second Boatman
Rebecca Charles as Chambermaid
Richard Gold as Lord in Waiting
Rachel Clarke as First Whore
Lucy Speed as Second Whore
Patricia Potter as Third Whore
John Ramm as Makepeace's Neighbour
Martin Neely as Paris / Lady Montague (as Martin Neeley)
The Choir of St. George's School in Windsor as (as The Choir of St George's School Windsor)
Rest of cast listed alphabetically:
Jason Anthony as Nobleman (uncredited)
Steve Apelt as Theatregoer (uncredited)
Paul Bannon as Theatre Attendee (uncredited)
Matthew Christian as Queen Elizabeth I Party Guest (uncredited)
Kelley Costigan as Theatregoer (uncredited)
Matthew Crosby as Theatregoer (uncredited)
Rupert Everett as Christopher Marlowe (uncredited)
Russell Greening as Thomas Crown (uncredited)
John Inman as Lady Capulet in Play (uncredited)
Carlisle Redthunder as Indian (uncredited)""",
            'country': 'United States',
            'genre': 'Comedy, Drama, History',
            'classification': False,
            'studio': 'Universal Pictures, Miramax, The Bedford Falls Company',
            'o_site': False,
            'site': 'https://www.imdb.com/title/tt0138097/',
            'trailer': 'https://www.imdb.com/title/tt0138097/trailers',
            'year': 1998,
            'notes': _('Language') + ': English\n'
                     + _('Audio') + ': Dolby Digital, DTS, SDDS\n'
                     + _('Color') + ': Color',
#             'notes': _('Language') + ': English\n'
#                      + _('Audio') + ': Dolby Digital, DTS, SDDS\n'
#                      + _('Color') + ': Color\n\
# Tagline: ...A Comedy About the Greatest Love Story Almost Never Told...\n\
# Love is the only inspiration',
            'runtime': 123,
            'image': True,
            'rating': 7.0,
            # 'screenplay': 'Marc Norman, Tom Stoppard',
            'screenplay': '',
            # 'cameraman': 'Richard Greatrex',
            'cameraman': '',
            'barcode': False
            },
        }
