// Copyright (C) 2024 Philipp Hofer
//
// Licensed under the EUPL, Version 1.2 or - as soon they will be approved by
// the European Commission - subsequent versions of the EUPL (the "Licence").
// You may not use this work except in compliance with the Licence.
//
// You should have received a copy of the European Union Public License along
// with this program.  If not, you may obtain a copy of the Licence at:
// <https://joinup.ec.europa.eu/software/page/eupl>
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the Licence is distributed on an "AS IS" basis,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the Licence for the specific language governing permissions and
// limitations under the Licence.

use std::collections::HashMap;
use std::iter::Peekable;

use roxmltree::{Children, Node};

use crate::law;
use crate::paragraph::parser::absatz::Absatz;
use crate::paragraph::parser::{AbsatzAbs, Content, Expect, Fzinhalt, Kzinhalt, Ueberschrift};

#[derive(Debug, PartialEq, Default)]
pub(crate) struct Abschnitt {
    metadata: HashMap<String, String>,
    pub(crate) cont: bool,
}

impl Abschnitt {
    pub(crate) fn parse(n: Node, builder: &mut law::Builder) -> Abschnitt {
        Expect::from(&n).tag("abschnitt");

        let mut ret = Abschnitt::default();
        let mut c = n.children().peekable();

        Self::skip_static_fields(&mut c);

        ret.handle_metadata(&mut c, builder);

        if !Self::handle_headers(&mut c, builder) {
            return ret;
        }

        let mut absatze = Vec::new();

        // Special handling of first paragraph (needs id)...
        let (par_id, first_abs) = Absatz::parse_full(&mut c);
        let Some(par_id) = par_id else {
            panic!("First paragraph needs to have an id, not found")
        };
        absatze.push(first_abs);

        // ... and then there can be as many 'Absätze' as our law-setter wants
        while let Some(child) = c.peek() {
            if AbsatzAbs::test(child) {
                let (_, absatz) = Absatz::parse_full(&mut c);
                absatze.push(absatz);
            } else {
                break;
            }
        }

        if absatze.len() == 1 {
            builder.new_par(par_id, absatze[0].clone());
        } else {
            let mut contents = Vec::new();
            contents.extend(absatze.iter().cloned());
            builder.new_par(par_id, Content::List(contents));
        }

        ret.handle_metadata(&mut c, builder);

        // Skip all UeberschriftTitle and Absatz
        while let Some(child) = c.peek() {
            if Ueberschrift::test(child, "titel") {
                c.next();
                continue;
            }
            if Absatz::test_with_typ(child, "erltext") {
                c.next();
                continue;
            }
            break;
        }

        Expect::empty(c.next());

        ret.cont = true;
        ret
    }

    // There are paragraph-specific meta-data at the top and bottom of each xml file. We parse
    // those. When we encounter the title "Text" the real content starts, we stop parsing meta
    // data.
    fn handle_metadata(&mut self, c: &mut Peekable<Children>, builder: &mut law::Builder) {
        while c.peek().is_some() {
            let key = Ueberschrift::parse(c.next().unwrap(), "titel").content;

            // We are done with meta-data parsing
            if key == "Text" {
                break;
            }

            let mut value = String::new();
            while let Some(child) = c.peek() {
                if Absatz::test_with_typ(child, "erltext") {
                    let absatz = Absatz::parse(c.next().unwrap());
                    value.push_str(&format!("{}\n", absatz.content));
                } else {
                    break;
                }
            }
            value = value.trim().into();
            assert!(
                !value.is_empty(),
                "Expected at least on erltext-absatz after title meta-data"
            );

            // We want ot use this information in our markdown output.
            // TODO: Use all metadata, instead of this specific call
            if key == "Beachte" {
                builder.add_next_para_note(value.clone());
            }

            self.metadata.insert(key, value);
        }
    }

    // At the beginning of each 'Abschnitt' there are 4 static fields. Since they don't provide any
    // value, we skip them with this function.
    // If they are not there, we panic (unwrap), as we should take a look why they changed that.
    fn skip_static_fields(node: &mut Peekable<Children>) {
        Kzinhalt::parse(node.next().unwrap()); // "Bundesrecht konsolidiert"
        Kzinhalt::parse(node.next().unwrap()); // "Bundesrecht konsolidiert"
        Fzinhalt::parse(node.next().unwrap()); // "www.ris.bka.gv.at" and "Seite X von Y"
        Fzinhalt::parse(node.next().unwrap()); // "www.ris.bka.gv.at" and "Seite X von Y"
    }

    // we have optionally headers. Such as "Einleitung", "Von den bürgerlichen Gesetzen üerhaupt,"
    // etc. If we have headers which indicate that we are done and we want to stop parsing
    // ("anlage" + "Artikel" we indicate this wish by returning false.
    fn handle_headers(c: &mut Peekable<Children>, builder: &mut law::Builder) -> bool {
        while let Some(child) = c.peek() {
            // Schiffahrtsgesetz: stop @ anlagen (for now)
            if Ueberschrift::test(child, "anlage") {
                return false;
            }
            if Ueberschrift::test(child, "g1") {
                let ueberschrift = Ueberschrift::parse(c.next().unwrap(), "g1");
                if ueberschrift.content.trim().starts_with("Artikel") {
                    return false;
                }
                builder.new_header(&ueberschrift.content);
            } else if Ueberschrift::test(child, "g2") {
                let ueberschrift = Ueberschrift::parse(c.next().unwrap(), "g2");
                builder.new_desc(&ueberschrift.content);
            } else if Ueberschrift::test(child, "g1min") {
                let ueberschrift = Ueberschrift::parse(c.next().unwrap(), "g1min");
                builder.new_header(&ueberschrift.content);
            } else if Ueberschrift::test(child, "art") {
                let ueberschrift = Ueberschrift::parse(c.next().unwrap(), "art");
                if ueberschrift.content.trim().starts_with("Artikel") {
                    return false;
                }
            } else {
                break;
            }
        }

        // while (not if) because we can have two subsequent paraheaders (e.g. § 405 abgb)
        while let Some(child) = c.peek() {
            if Ueberschrift::test(child, "para") {
                builder
                    .new_next_para_header(&Ueberschrift::parse(c.next().unwrap(), "para").content);
                continue;
            }
            break;
        }
        true
    }
}
