use crate::{cmd::oid::ObjectId, rpc::proto as p};

impl From<ObjectId> for p::ObjectId {
    fn from(oid: ObjectId) -> Self {
        p::ObjectId { value: oid.0 }
    }
}

impl From<p::ObjectId> for ObjectId {
    fn from(oid: p::ObjectId) -> Self {
        ObjectId::new(&oid.value)
    }
}

impl From<&ObjectId> for p::ObjectId {
    fn from(oid: &ObjectId) -> Self {
        p::ObjectId {
            value: oid.0.clone(),
        }
    }
}

impl From<crate::cmd::commit::CommitSignature> for p::CommitSignature {
    fn from(sig: crate::cmd::commit::CommitSignature) -> Self {
        p::CommitSignature {
            name: sig.name,
            email: sig.email,
            time_secs: sig.time_secs,
            offset_minutes: sig.offset_minutes,
        }
    }
}

impl From<p::CommitSignature> for crate::cmd::commit::CommitSignature {
    fn from(sig: p::CommitSignature) -> Self {
        crate::cmd::commit::CommitSignature {
            name: sig.name,
            email: sig.email,
            time_secs: sig.time_secs,
            offset_minutes: sig.offset_minutes,
        }
    }
}

impl From<crate::cmd::tagger::GitTagger> for p::GitTagger {
    fn from(t: crate::cmd::tagger::GitTagger) -> Self {
        p::GitTagger {
            name: t.name,
            email: t.email,
        }
    }
}

impl From<p::GitTagger> for crate::cmd::tagger::GitTagger {
    fn from(t: p::GitTagger) -> Self {
        crate::cmd::tagger::GitTagger {
            name: t.name,
            email: t.email,
        }
    }
}

impl From<crate::cmd::commit::CommitMeta> for p::CommitMeta {
    fn from(c: crate::cmd::commit::CommitMeta) -> Self {
        p::CommitMeta {
            oid: Some(c.oid.into()),
            message: c.message,
            summary: c.summary,
            author: Some(c.author.into()),
            committer: Some(c.committer.into()),
            tree_id: Some(c.tree_id.into()),
            parent_ids: c.parent_ids.into_iter().map(Into::into).collect(),
            encoding: c.encoding,
        }
    }
}

impl From<crate::cmd::commit::CommitRefInfo> for p::CommitRefInfo {
    fn from(r: crate::cmd::commit::CommitRefInfo) -> Self {
        p::CommitRefInfo {
            name: r.name,
            target: Some(r.target.into()),
            is_remote: r.is_remote,
            is_tag: r.is_tag,
        }
    }
}

impl From<crate::cmd::commit::CommitSummary> for p::CommitSummary {
    fn from(s: crate::cmd::commit::CommitSummary) -> Self {
        p::CommitSummary {
            head: s.head.map(Into::into),
            count: s.count as u64,
        }
    }
}

impl From<crate::cmd::commit::CommitWalkSort> for p::CommitWalkSort {
    fn from(s: crate::cmd::commit::CommitWalkSort) -> Self {
        match s {
            crate::cmd::commit::CommitWalkSort::None => p::CommitWalkSort::None,
            crate::cmd::commit::CommitWalkSort::Topological => {
                p::CommitWalkSort::Topological
            }
            crate::cmd::commit::CommitWalkSort::Time => p::CommitWalkSort::Time,
            crate::cmd::commit::CommitWalkSort::Reverse => {
                p::CommitWalkSort::Reverse
            }
        }
    }
}

impl From<p::CommitWalkSort> for crate::cmd::commit::CommitWalkSort {
    fn from(s: p::CommitWalkSort) -> Self {
        match s {
            p::CommitWalkSort::None => crate::cmd::commit::CommitWalkSort::None,
            p::CommitWalkSort::Topological => {
                crate::cmd::commit::CommitWalkSort::Topological
            }
            p::CommitWalkSort::Time => crate::cmd::commit::CommitWalkSort::Time,
            p::CommitWalkSort::Reverse => {
                crate::cmd::commit::CommitWalkSort::Reverse
            }
        }
    }
}

impl From<i32> for crate::cmd::commit::CommitWalkSort {
    fn from(val: i32) -> Self {
        let proto_sort: p::CommitWalkSort =
            p::CommitWalkSort::try_from(val).unwrap_or(p::CommitWalkSort::Time);
        proto_sort.into()
    }
}

impl From<p::CommitWalkParams> for crate::cmd::commit::CommitWalkParams {
    fn from(p: p::CommitWalkParams) -> Self {
        crate::cmd::commit::CommitWalkParams {
            start_oids: p.start_oids.into_iter().map(Into::into).collect(),
            hide_oids: p.hide_oids.into_iter().map(Into::into).collect(),
            limit: p.limit.map(|l| l as usize),
            skip: p.skip as usize,
            first_parent: p.first_parent,
            sort: p.sort.into(),
            branch: None,
        }
    }
}

impl From<p::CommitCherryPickParams>
    for crate::cmd::commit::CommitCherryPickParams
{
    fn from(p: p::CommitCherryPickParams) -> Self {
        crate::cmd::commit::CommitCherryPickParams {
            cherrypick_oid: p.cherrypick_oid.unwrap_or_default().into(),
            author: p.author.unwrap_or_default().into(),
            committer: p.committer.unwrap_or_default().into(),
            message: p.message,
            mainline: p.mainline,
            update_ref: p.update_ref,
        }
    }
}

impl From<p::CommitCherryPickSequence>
    for crate::cmd::commit::CommitCherryPickSequence
{
    fn from(p: p::CommitCherryPickSequence) -> Self {
        crate::cmd::commit::CommitCherryPickSequence {
            cherrypick_oids: p
                .cherrypick_oids
                .into_iter()
                .map(Into::into)
                .collect(),
            author: p.author.unwrap_or_default().into(),
            committer: p.committer.unwrap_or_default().into(),
            update_ref: p.update_ref,
        }
    }
}

impl From<crate::cmd::branch::BranchListItem> for p::BranchListItem {
    fn from(b: crate::cmd::branch::BranchListItem) -> Self {
        p::BranchListItem {
            name: b.name,
            oid: Some(b.oid.into()),
            is_head: b.is_head,
            is_remote: b.is_remote,
            is_current: b.is_current,
            upstream: b.upstream,
        }
    }
}

impl From<crate::cmd::branch::BranchSummary> for p::BranchSummary {
    fn from(s: crate::cmd::branch::BranchSummary) -> Self {
        p::BranchSummary {
            local_count: s.local_count as u64,
            remote_count: s.remote_count as u64,
            all_count: s.all_count as u64,
        }
    }
}

impl From<p::BranchForkParams> for crate::cmd::branch::BranchForkParams {
    fn from(p: p::BranchForkParams) -> Self {
        crate::cmd::branch::BranchForkParams {
            name: p.name,
            oid: p.oid.unwrap_or_default().into(),
            force: p.force,
        }
    }
}

impl From<p::BranchDeleteParams> for crate::cmd::branch::BranchDeleteParams {
    fn from(p: p::BranchDeleteParams) -> Self {
        crate::cmd::branch::BranchDeleteParams {
            name: p.name,
            force: p.force,
        }
    }
}

impl From<p::BranchReNameParams> for crate::cmd::branch::BranchReNameParams {
    fn from(p: p::BranchReNameParams) -> Self {
        crate::cmd::branch::BranchReNameParams {
            old_branch: p.old_branch,
            new_branch: p.new_branch,
            force: p.force,
        }
    }
}

impl From<crate::cmd::diff::DiffDeltaStatus> for p::DiffDeltaStatus {
    fn from(s: crate::cmd::diff::DiffDeltaStatus) -> Self {
        match s {
            crate::cmd::diff::DiffDeltaStatus::Unmodified => {
                p::DiffDeltaStatus::Unmodified
            }
            crate::cmd::diff::DiffDeltaStatus::Added => {
                p::DiffDeltaStatus::Added
            }
            crate::cmd::diff::DiffDeltaStatus::Deleted => {
                p::DiffDeltaStatus::Deleted
            }
            crate::cmd::diff::DiffDeltaStatus::Modified => {
                p::DiffDeltaStatus::Modified
            }
            crate::cmd::diff::DiffDeltaStatus::Renamed => {
                p::DiffDeltaStatus::Renamed
            }
            crate::cmd::diff::DiffDeltaStatus::Copied => {
                p::DiffDeltaStatus::Copied
            }
            crate::cmd::diff::DiffDeltaStatus::Typechange => {
                p::DiffDeltaStatus::Typechange
            }
            crate::cmd::diff::DiffDeltaStatus::Conflicted => {
                p::DiffDeltaStatus::Conflicted
            }
        }
    }
}

impl From<crate::cmd::diff::DiffFile> for p::DiffFile {
    fn from(f: crate::cmd::diff::DiffFile) -> Self {
        p::DiffFile {
            oid: f.oid.map(Into::into),
            path: f.path,
            size: f.size,
            is_binary: f.is_binary,
        }
    }
}

impl From<crate::cmd::diff::DiffHunk> for p::DiffHunk {
    fn from(h: crate::cmd::diff::DiffHunk) -> Self {
        p::DiffHunk {
            old_start: h.old_start,
            old_lines: h.old_lines,
            new_start: h.new_start,
            new_lines: h.new_lines,
            header: h.header,
        }
    }
}

impl From<crate::cmd::diff::DiffLine> for p::DiffLine {
    fn from(l: crate::cmd::diff::DiffLine) -> Self {
        p::DiffLine {
            content: l.content,
            origin: l.origin.to_string(),
            old_lineno: l.old_lineno,
            new_lineno: l.new_lineno,
            num_lines: l.num_lines,
            content_offset: l.content_offset,
        }
    }
}

impl From<crate::cmd::diff::DiffDelta> for p::DiffDelta {
    fn from(d: crate::cmd::diff::DiffDelta) -> Self {
        let status_proto: p::DiffDeltaStatus = d.status.into();
        p::DiffDelta {
            status: status_proto as i32,
            old_file: Some(d.old_file.into()),
            new_file: Some(d.new_file.into()),
            nfiles: d.nfiles as u32,
            hunks: d.hunks.into_iter().map(Into::into).collect(),
            lines: d.lines.into_iter().map(Into::into).collect(),
        }
    }
}

impl From<crate::cmd::diff::DiffStats> for p::DiffStats {
    fn from(s: crate::cmd::diff::DiffStats) -> Self {
        p::DiffStats {
            files_changed: s.files_changed as u64,
            insertions: s.insertions as u64,
            deletions: s.deletions as u64,
        }
    }
}

impl From<crate::cmd::diff::DiffResult> for p::DiffResult {
    fn from(r: crate::cmd::diff::DiffResult) -> Self {
        p::DiffResult {
            stats: Some(r.stats.into()),
            deltas: r.deltas.into_iter().map(Into::into).collect(),
        }
    }
}

impl From<p::DiffOptions> for crate::cmd::diff::DiffOptions {
    fn from(o: p::DiffOptions) -> Self {
        crate::cmd::diff::DiffOptions {
            context_lines: o.context_lines,
            pathspec: o.pathspec,
            ignore_whitespace: o.ignore_whitespace,
            force_text: o.force_text,
            reverse: o.reverse,
        }
    }
}

impl From<crate::cmd::diff::SideBySideChangeType> for p::SideBySideChangeType {
    fn from(t: crate::cmd::diff::SideBySideChangeType) -> Self {
        match t {
            crate::cmd::diff::SideBySideChangeType::Unchanged => {
                p::SideBySideChangeType::Unchanged
            }
            crate::cmd::diff::SideBySideChangeType::Added => {
                p::SideBySideChangeType::Added
            }
            crate::cmd::diff::SideBySideChangeType::Removed => {
                p::SideBySideChangeType::Removed
            }
            crate::cmd::diff::SideBySideChangeType::Modified => {
                p::SideBySideChangeType::Modified
            }
            crate::cmd::diff::SideBySideChangeType::Empty => {
                p::SideBySideChangeType::Empty
            }
        }
    }
}

impl From<crate::cmd::diff::SideBySideLine> for p::SideBySideLine {
    fn from(l: crate::cmd::diff::SideBySideLine) -> Self {
        let change_type_proto: p::SideBySideChangeType = l.change_type.into();
        p::SideBySideLine {
            left_line_no: l.left_line_no,
            right_line_no: l.right_line_no,
            left_content: l.left_content,
            right_content: l.right_content,
            change_type: change_type_proto as i32,
        }
    }
}

impl From<crate::cmd::diff::SideBySideFile> for p::SideBySideFile {
    fn from(f: crate::cmd::diff::SideBySideFile) -> Self {
        p::SideBySideFile {
            path: f.path,
            additions: f.additions as u64,
            deletions: f.deletions as u64,
            is_binary: f.is_binary,
            is_rename: f.is_rename,
            lines: f.lines.into_iter().map(Into::into).collect(),
        }
    }
}

impl From<crate::cmd::diff::SideBySideDiffResult> for p::SideBySideDiffResult {
    fn from(r: crate::cmd::diff::SideBySideDiffResult) -> Self {
        p::SideBySideDiffResult {
            files: r.files.into_iter().map(Into::into).collect(),
            total_additions: r.total_additions as u64,
            total_deletions: r.total_deletions as u64,
        }
    }
}

impl From<crate::cmd::merge::MergeAnalysisResult> for p::MergeAnalysisResult {
    fn from(r: crate::cmd::merge::MergeAnalysisResult) -> Self {
        p::MergeAnalysisResult {
            is_none: r.is_none,
            is_normal: r.is_normal,
            is_up_to_date: r.is_up_to_date,
            is_fast_forward: r.is_fast_forward,
            is_unborn: r.is_unborn,
        }
    }
}

impl From<crate::cmd::merge::MergePreferenceResult>
    for p::MergePreferenceResult
{
    fn from(r: crate::cmd::merge::MergePreferenceResult) -> Self {
        p::MergePreferenceResult {
            is_none: r.is_none,
            is_no_fast_forward: r.is_no_fast_forward,
            is_fastforward_only: r.is_fastforward_only,
        }
    }
}

impl From<crate::cmd::merge::MergeOptions> for p::MergeOptions {
    fn from(o: crate::cmd::merge::MergeOptions) -> Self {
        p::MergeOptions {
            find_renames: o.find_renames,
            fail_on_conflict: o.fail_on_conflict,
            skip_reuc: o.skip_reuc,
            no_recursive: o.no_recursive,
            rename_threshold: o.rename_threshold,
            target_limit: o.target_limit,
            recursion_limit: o.recursion_limit,
        }
    }
}

impl From<p::MergeOptions> for crate::cmd::merge::MergeOptions {
    fn from(o: p::MergeOptions) -> Self {
        crate::cmd::merge::MergeOptions {
            find_renames: o.find_renames,
            fail_on_conflict: o.fail_on_conflict,
            skip_reuc: o.skip_reuc,
            no_recursive: o.no_recursive,
            rename_threshold: o.rename_threshold,
            target_limit: o.target_limit,
            recursion_limit: o.recursion_limit,
        }
    }
}

impl From<p::MergeCommitParams> for crate::cmd::merge::MergeCommitParams {
    fn from(p: p::MergeCommitParams) -> Self {
        crate::cmd::merge::MergeCommitParams {
            their_commit: p.their_commit.unwrap_or_default().into(),
            author: p.author.unwrap_or_default().into(),
            committer: p.committer.unwrap_or_default().into(),
            message: p.message,
            update_ref: p.update_ref,
            options: p.options.map(Into::into),
        }
    }
}

impl From<crate::cmd::merge::MergeTreeResult> for p::MergeTreeResult {
    fn from(r: crate::cmd::merge::MergeTreeResult) -> Self {
        p::MergeTreeResult {
            tree_id: Some(r.tree_id.into()),
            has_conflicts: r.has_conflicts,
        }
    }
}

impl From<p::SquashCommitParams> for crate::cmd::merge::SquashCommitParams {
    fn from(p: p::SquashCommitParams) -> Self {
        crate::cmd::merge::SquashCommitParams {
            their_commit: p.their_commit.unwrap_or_default().into(),
            options: p.options.map(Into::into),
        }
    }
}

impl From<crate::cmd::tag::TagItem> for p::TagItem {
    fn from(t: crate::cmd::tag::TagItem) -> Self {
        p::TagItem {
            name: t.name,
            oid: Some(t.oid.into()),
            target: Some(t.target.into()),
            is_annotated: t.is_annotated,
            message: t.message,
            tagger: t.tagger,
            tagger_email: t.tagger_email,
        }
    }
}

impl From<crate::cmd::tag::TagSummary> for p::TagSummary {
    fn from(s: crate::cmd::tag::TagSummary) -> Self {
        p::TagSummary {
            total_count: s.total_count as u64,
        }
    }
}

impl From<p::TagInitParams> for crate::cmd::tag::TagInitParams {
    fn from(p: p::TagInitParams) -> Self {
        crate::cmd::tag::TagInitParams {
            name: p.name,
            target: p.target.unwrap_or_default().into(),
            message: p.message,
            tagger: p.tagger.map(Into::into),
            force: p.force,
        }
    }
}

impl From<p::TagDeleteParams> for crate::cmd::tag::TagDeleteParams {
    fn from(p: p::TagDeleteParams) -> Self {
        crate::cmd::tag::TagDeleteParams { name: p.name }
    }
}

impl From<p::TagRenameParams> for crate::cmd::tag::TagRenameParams {
    fn from(p: p::TagRenameParams) -> Self {
        crate::cmd::tag::TagRenameParams {
            old_name: p.old_name,
            new_name: p.new_name,
            force: p.force,
        }
    }
}

impl From<p::TagUpdateMessageParams>
    for crate::cmd::tag::TagUpdateMessageParams
{
    fn from(p: p::TagUpdateMessageParams) -> Self {
        crate::cmd::tag::TagUpdateMessageParams {
            name: p.name,
            message: p.message,
            tagger: p.tagger.unwrap_or_default().into(),
            force: p.force,
        }
    }
}

impl From<crate::cmd::tree::TreeKind> for p::TreeKind {
    fn from(k: crate::cmd::tree::TreeKind) -> Self {
        match k {
            crate::cmd::tree::TreeKind::Blob => p::TreeKind::Blob,
            crate::cmd::tree::TreeKind::Tree => p::TreeKind::Tree,
            crate::cmd::tree::TreeKind::LfsPointer => p::TreeKind::LfsPointer,
        }
    }
}

impl From<crate::cmd::tree::TreeInfo> for p::TreeInfo {
    fn from(i: crate::cmd::tree::TreeInfo) -> Self {
        p::TreeInfo {
            oid: Some(i.oid.into()),
            entry_count: i.entry_count as u64,
            is_empty: i.is_empty,
        }
    }
}

impl From<crate::cmd::tree::TreeEntry> for p::TreeEntry {
    fn from(e: crate::cmd::tree::TreeEntry) -> Self {
        let kind_proto: p::TreeKind = e.kind.into();
        p::TreeEntry {
            name: e.name,
            oid: Some(e.oid.into()),
            kind: kind_proto as i32,
            filemode: e.filemode,
            is_binary: e.is_binary,
            is_lfs: e.is_lfs,
            last_commit_message: String::new(),
            last_commit_time: String::new(),
            last_commit_author_name: String::new(),
            last_commit_author_email: String::new(),
        }
    }
}

impl From<crate::cmd::blame::CommitBlameHunk> for p::CommitBlameHunk {
    fn from(h: crate::cmd::blame::CommitBlameHunk) -> Self {
        p::CommitBlameHunk {
            commit_oid: Some(h.commit_oid.into()),
            final_start_line: h.final_start_line,
            final_lines: h.final_lines,
            orig_start_line: h.orig_start_line,
            orig_lines: h.orig_lines,
            boundary: h.boundary,
            orig_path: h.orig_path,
        }
    }
}

impl From<crate::cmd::blame::CommitBlameLine> for p::CommitBlameLine {
    fn from(l: crate::cmd::blame::CommitBlameLine) -> Self {
        p::CommitBlameLine {
            commit_oid: Some(l.commit_oid.into()),
            line_no: l.line_no,
            content: l.content,
            orig_path: l.orig_path,
        }
    }
}

impl From<p::BlameOptions> for crate::cmd::blame::BlameOptions {
    fn from(o: p::BlameOptions) -> Self {
        crate::cmd::blame::BlameOptions {
            min_line: o.min_line.map(|l| l as usize),
            max_line: o.max_line.map(|l| l as usize),
            track_copies_same_file: o.track_copies_same_file,
            track_copies_same_commit_moves: o.track_copies_same_commit_moves,
            ignore_whitespace: o.ignore_whitespace,
        }
    }
}

impl From<p::ArchiveOptions> for crate::cmd::archive::ArchiveOptions {
    fn from(o: p::ArchiveOptions) -> Self {
        crate::cmd::archive::ArchiveOptions {
            tree: o.tree.unwrap_or_default().into(),
            prefix: o.prefix,
            pathspec: o.pathspec,
        }
    }
}

impl From<p::BlobLoadParams> for crate::cmd::blob::BlobLoadParams {
    fn from(p: p::BlobLoadParams) -> Self {
        crate::cmd::blob::BlobLoadParams {
            id: p.id.unwrap_or_default().into(),
            path: p.path,
        }
    }
}

impl From<p::BlobSizeParams> for crate::cmd::blob::BlobSizeParams {
    fn from(p: p::BlobSizeParams) -> Self {
        crate::cmd::blob::BlobSizeParams {
            id: p.id.unwrap_or_default().into(),
            path: p.path,
        }
    }
}

impl From<p::BlobUploadParams> for crate::cmd::blob::BlobUploadParams {
    fn from(p: p::BlobUploadParams) -> Self {
        crate::cmd::blob::BlobUploadParams {
            blob: p.blob,
            path: p.path,
        }
    }
}

impl From<p::BlobChunkParam> for crate::cmd::blob::BlobChunkParam {
    fn from(p: p::BlobChunkParam) -> Self {
        crate::cmd::blob::BlobChunkParam {
            path: p.path,
            oid: p.oid.unwrap_or_default().into(),
            size: p.size as usize,
            offset: p.offset as usize,
        }
    }
}

impl From<crate::cmd::blob::BlobChunk> for p::BlobChunk {
    fn from(c: crate::cmd::blob::BlobChunk) -> Self {
        p::BlobChunk {
            param: Some(c.param.into()),
            chunk: c.chunk,
        }
    }
}

impl From<p::InitRepoParams> for crate::cmd::init::InitRepositoriesParams {
    fn from(p: p::InitRepoParams) -> Self {
        crate::cmd::init::InitRepositoriesParams {
            namespace: p.namespace,
            repo_name: p.repo_name,
            default_branch: p.default_branch,
            description: p.description,
            initialize_with_readme: p.initialize_with_readme,
            enable_lfs: p.enable_lfs,
        }
    }
}

impl From<crate::cmd::blob::BlobChunkParam> for p::BlobChunkParam {
    fn from(p: crate::cmd::blob::BlobChunkParam) -> Self {
        p::BlobChunkParam {
            path: p.path,
            oid: Some(p.oid.into()),
            size: p.size as u64,
            offset: p.offset as u64,
        }
    }
}
