
import json
from pathlib import Path
from typing import Final, Generator, Iterable, Optional
from bs4 import BeautifulSoup


METADATA_FILE_NAME: Final[str] = 'metadata.json'
DESCRIPTION_FILE_NAME: Final[str] = 'rule.html'

class LanguageSpecificRule:
  language_path: Final[Path]
  rule: 'GenericRule'
  __metadata: Optional[dict] = None
  __description: Optional[object] = None

  def __init__(self, language_path: Path, rule: 'GenericRule'):
    self.language_path = language_path
    self.rule = rule

  @property
  def language(self):
    return self.language_path.name

  @property
  def id(self):
    return f'{self.language}:{self.rule.id}'

  @property
  def metadata(self):
    if self.__metadata is not None:
      return self.__metadata
    metadata_path = self.language_path.joinpath(METADATA_FILE_NAME)
    lang_metadata = json.loads(metadata_path.read_bytes())
    self.__metadata = self.rule.generic_metadata | lang_metadata
    return self.__metadata

  @property
  def description(self):
    if self.__description is not None:
      return self.__description
    description_path = self.language_path.joinpath(DESCRIPTION_FILE_NAME)
    soup = BeautifulSoup(description_path.read_bytes(),features="html.parser")
    self.__description = soup
    return self.__description

class GenericRule:
  rule_path: Final[Path]
  __generic_metadata: Optional[dict] = None

  def __init__(self, rule_path: Path):
    self.rule_path = rule_path

  @property
  def id(self) -> str:
    return self.rule_path.name

  @property
  def specializations(self) -> Generator[LanguageSpecificRule, None, None]:
    return (LanguageSpecificRule(child, self) for child in self.rule_path.iterdir() if child.is_dir())
  
  def get_language(self, language: str) -> LanguageSpecificRule:
    return LanguageSpecificRule(self.rule_path.joinpath(language), self)

  @property
  def generic_metadata(self):
    if self.__generic_metadata is not None:
      return self.__generic_metadata
    metadata_path = self.rule_path.joinpath(METADATA_FILE_NAME)
    self.__generic_metadata = json.loads(metadata_path.read_bytes())
    return self.__generic_metadata


class RulesRepository:
  DEFAULT_RULES_PATH: Final[Path] = Path(__file__).parent.parent.parent.joinpath('rules')

  rules_path: Final[Path]

  def __init__(self, *, rules_path: Path=DEFAULT_RULES_PATH):
    print(rules_path.absolute().__str__())
    self.rules_path = rules_path

  @property
  def rules(self) -> Generator[GenericRule, None, None]:
    return (GenericRule(child) for child in self.rules_path.glob('S*') if child.is_dir())
    
  def get_rule(self, ruleid: str):
    return GenericRule(self.rules_path.joinpath(ruleid))
